75

Assuming i have:

<li id="1">Mary</li>
<li id="2">John, Mary, Dave</li>
<li id="3">John, Dave, Mary</li>
<li id="4">John</li>

If i need to find all <li> Elements which contain "John" and "Mary", how would i construct the jQuery?

A search for a single string seems easy:

$('li:contains("John")').text()

I am looking for something like the following pseudo code:

$('li:contains("John")' && 'li:contains("Mary")').text()

Thanks!

zıəs uɐɟəʇs
  • 1,728
  • 3
  • 14
  • 27

4 Answers4

133

Answer

To find li's that have text containing BOTH Mary AND John:

$('li:contains("Mary"):contains("John")')

To find li's that have text containing EITHER Mary OR John:

$('li:contains("Mary"), li:contains("John")')

Explanation

Just think of the :contains as if it was a class declaration, like .class:

$('li.one.two').      // Find <li>'s with classes of BOTH one AND two
$('li.one, li.two').  // Find <li>'s with a class of EITHER one OR two

It's the same with :contains:

$('li:contains("Mary"):contains("John")').      // Both Mary AND John
$('li:contains("Mary"), li:contains("John")').  // Either Mary OR John

Demo

http://jsbin.com/ejuzi/edit

Danny Beckett
  • 20,529
  • 24
  • 107
  • 134
Adam Kiss
  • 11,811
  • 9
  • 48
  • 81
  • How could I go about spitting out the contains if I have the strings in an Array? The array will be built from checkbox values so it doesn't have a standard length each time? – jeynon Aug 19 '20 at 21:03
8

Answer

The correct syntax would be $("li:contains('John'),li:contains('Mary')").css("color","red")

But I found out that if you had many cases to test, jQuery will perform very badly (especially on IE6, I know, it's old but still in use). So I decided to write my own attribute filter.

This is how to use it: $("li:mcontains('John','Mary')").css("color","red")

Code

jQuery.expr[':'].mcontains = function(obj, index, meta, stack){
    result = false;     
    theList = meta[3].split("','");

    var contents = (obj.textContent || obj.innerText || jQuery(obj).text() || '')

    for (x=0;x<theList.length;x++) {
        if (contents.indexOf(theList[x]) >= 0) {
            return true;
        }
    }

    return false;
};
Community
  • 1
  • 1
Kristof
  • 97
  • 1
  • 1
  • 3
    add `.toLowerCase()` to `contents` and `theList[x]` to make this case insensitive. – Eonasdan Sep 25 '12 at 16:00
  • how would you make it case insensitive? – computerguy Jun 23 '15 at 11:26
  • Thanks - this was what I wanted in Chrome : But it was driving me a bit nuts, with three entries it would only match the middle one : `$("li:mcontains('John', 'Ringo', 'Mary')").css("color","red");` After a bit of debugging I found ` theList = meta[3].split("','");` a) I was using ',' needed to remove the space eg: ',' b) Split was giving 'John, Ringo , Mary' (note single quotes on the first and last entry) Then: `$("li:mcontains(John', 'Ringo', 'Mary)").css("color","red");` did work!! Happy to be pointed out what I am doing wrong - but that was what I found – Mark O'Donohue Jun 29 '17 at 04:10
7

How about

$('li:contains("John"),li:contains("Mary")')
Lazarus
  • 41,906
  • 4
  • 43
  • 54
  • And jQuery doesn't even index anything twice. So there's an automatic filter and Lazarus' answer really finds four elements. – Paul Mar 10 '10 at 12:42
  • Thanks, that is what i was hoping would work, but it doesn't. This selector doesn't return any results. I made a quick try with: $('li:contains("John")','li:contains("Mary")').text() and all i got back was "". Even $('li:contains("John")','li:contains("Mary")').css('font-color','red') didn't seem to work. – zıəs uɐɟəʇs Mar 10 '10 at 12:45
  • 1
    I think your are inserting a `'` in between the two `li:contains`. – rahul Mar 10 '10 at 12:46
  • So, even correcting the bogus syntax i had in my tests mentioned above, this will still select all the
  • elements. $('li:contains("John"),li:contains("Mary")').css('color','red') And this (single quotes separating the selectors): $('li:contains("John")','li:contains("Mary")').css('color','red') doesn't select anything at all.
  • – zıəs uɐɟəʇs Mar 10 '10 at 12:52
  • Ah... I see what you mean, you only want LI that has *both* pieces of text... hmmmm... I'll need to think about that. – Lazarus Mar 10 '10 at 13:03
  • Oh gosh, this cold really affected my brain. Of course it works like Lazarus says, but i didn't make my question very clear. I wanted to select only
  • elements, which contain both strings, "John" and "Mary". Is that possible at all? BTW: this was my first post to stackoverflow and i must say, it works extremely well and blindingly fast. Thanks so much!
  • – zıəs uɐɟəʇs Mar 10 '10 at 13:07
  • Exactly Lazarus. Sorry for being a so vague in my Q. Blame it on my cold. – zıəs uɐɟəʇs Mar 10 '10 at 13:08
  • @Stefan: Answered & explained – Adam Kiss Mar 10 '10 at 13:16
  • @Stefan Stack Overflow is your friend :) – Mark Schultheiss Mar 10 '10 at 13:20