186

All right, I wonder if there is a way to make the :contains() jQuery's selector to select elements with only the string that is typed in

for example -

<p>hello</p>
<p>hello world</p>

$('p:contains("hello")').css('font-weight', 'bold');

The selector will select both p elements and make them bold, but I want it to select only the first one.

julian
  • 4,634
  • 10
  • 42
  • 59

8 Answers8

243

No, there's no jQuery (or CSS) selector that does that.

You can readily use filter:

$("p").filter(function() {
    return $(this).text() === "hello";
}).css("font-weight", "bold");

It's not a selector, but it does the job. :-)

If you want to handle whitespace before or after the "hello", you might throw a $.trim in there:

return $.trim($(this).text()) === "hello";

For the premature optimizers out there, if you don't care that it doesn't match <p><span>hello</span></p> and similar, you can avoid the calls to $ and text by using innerHTML directly:

return this.innerHTML === "hello";

...but you'd have to have a lot of paragraphs for it to matter, so many that you'd probably have other issues first. :-)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 7
    Might also want to `$.trim` it of any stray whitespace before making the comparison. – Blazemonger Mar 12 '13 at 14:43
  • For some reason I had to use "==" for this to work instead of "===". – Stephan B Jan 22 '15 at 05:36
  • 3
    @Stephan: If you were comparing with a number, you'd have to either use `==` (`== 2`) or put the number in a string (`=== "2"`), because the value you get from `text()` or `innerHTML` is always a string. If you were comparing with a string, it doesn't matter whether you use `==` or `===`. – T.J. Crowder Jan 22 '15 at 07:38
  • 1
    Its a shame that jQuery has implemented `:contains` wich is kind more complex, but hasn't implemented a selector for exact text match. ={ – Paulo Bueno Jul 17 '19 at 13:38
62

Try add a extend pseudo function:

$.expr[':'].textEquals = $.expr.createPseudo(function(arg) {
    return function( elem ) {
        return $(elem).text().match("^" + arg + "$");
    };
});

Then you can do:

$('p:textEquals("Hello World")');
Amadu Bah
  • 2,919
  • 28
  • 27
  • 4
    great solution: little addition: to make sure you don't overwrite an existing expr, I would do:```$.expr[':'].textEquals = $.expr[':'].textEquals || $.expr.createPseudo(function(arg) {``` – Mischa Molhoek Mar 08 '18 at 12:46
  • 2
    A little simpler to do this: `$(elem).text() == arg;`. Also works better if `arg` has characters that need to be escaped. – new name Sep 09 '21 at 18:44
13

You can use jQuery's filter() function to achieve this.

$("p").filter(function() {
// Matches exact string   
return $(this).text() === "Hello World";
}).css("font-weight", "bold");
dsgriffin
  • 66,495
  • 17
  • 137
  • 137
10

So Amandu's answer mostly works. Using it in the wild, however, I ran into some issues, where things that I would have expected to get found were not getting found. This was because sometimes there is random white space surrounding the element's text. It is my belief that if you're searching for "Hello World", you would still want it to match " Hello World ", or even "Hello World \n". Thus, I just added the "trim()" method to the function, which removes surrounding whitespace, and it started to work better.

Specifically...

$.expr[':'].textEquals = function(el, i, m) {
    var searchText = m[3];
    var match = $(el).text().trim().match("^" + searchText + "$")
    return match && match.length > 0;
}

Also, note, this answer is extremely similar to Select link by text (exact match)

And secondary note... trim only removes whitespace before and after the searched text. It does not remove whitespace in the middle of the words. I believe this is desirable behavior, but you could change that if you wanted.

Community
  • 1
  • 1
bwest87
  • 1,223
  • 13
  • 12
5

I found a way that works for me. It is not 100% exact but it eliminates all strings that contain more than just the word I am looking for because I check for the string not containing individual spaces too. By the way you don't need these " ". jQuery knows you are looking for a string. Make sure you only have one space in the :contains( ) part otherwise it won't work.

<p>hello</p>
<p>hello world</p>
$('p:contains(hello):not(:contains( ))').css('font-weight', 'bold');

And yes I know it won't work if you have stuff like <p>helloworld</p>

rf1234
  • 1,510
  • 12
  • 13
  • 1
    Clever usage of simple matches with contains(..) without using a filter or extension! Won't work on anything requiring spaces in the first contains(..), but trying to come up with a solution for that. Thanks! – JoePC Jul 30 '18 at 16:40
4

Like T.J. Crowder stated above, the filter function does wonders. It wasn't working for me in my specific case. I needed to search multiple tables and their respective td tags inside a div (in this case a jQuery dialog).

$("#MyJqueryDialog table tr td").filter(function () {
    // The following implies that there is some text inside the td tag.
    if ($.trim($(this).text()) == "Hello World!") {
       // Perform specific task.
    }
});

I hope this is helpful to someone!

Greg A
  • 531
  • 4
  • 9
  • I'm pretty new to jquery, but would think it'd be slightly better to use each() instead of filter() since the goal of filter is to return a subarray. But inspired the solution definitely, as I had the same issues :) – JeopardyTempest Jun 11 '18 at 14:46
1

An one-liner that works with alternative libraries to jQuery:

$('p').filter((i, p) => $(p).text().trim() === "hello").css('font-weight', 'bold');

And this is the equivalent to a jQuery's a:contains("pattern") selector:

var res = $('a').filter((i, a) => $(a).text().match(/pattern/));
leogama
  • 898
  • 9
  • 13
-4

The .first() will help here

$('p:contains("hello")').first().css('font-weight', 'bold');
tymeJV
  • 103,943
  • 14
  • 161
  • 157