252

Say a web page has a string such as "I am a simple string" that I want to find. How would I go about this using JQuery?

Reinstate Monica Please
  • 11,123
  • 3
  • 27
  • 48
Keith Donegan
  • 26,213
  • 34
  • 94
  • 129

7 Answers7

340

jQuery has the contains method. Here's a snippet for you:

<script type="text/javascript">
$(function() {
    var foundin = $('*:contains("I am a simple string")');
});
</script>

The selector above selects any element that contains the target string. The foundin will be a jQuery object that contains any matched element. See the API information at: https://api.jquery.com/contains-selector/

One thing to note with the '*' wildcard is that you'll get all elements, including your html an body elements, which you probably don't want. That's why most of the examples at jQuery and other places use $('div:contains("I am a simple string")')

user202729
  • 3,358
  • 3
  • 25
  • 36
Tony Miller
  • 9,059
  • 2
  • 27
  • 46
  • 12
    @Tony, how would I go about manipulating just the matching text and not just the whole parapgraph, div etc etc? – Keith Donegan May 29 '09 at 16:05
  • 12
    Should point out that this is case-sensitive. A user named "me" wrote this on the jQuery docs page: $.expr[':'].icontains = function(obj, index, meta, stack){ return (obj.textContent || obj.innerText || jQuery(obj).text() || '').toLowerCase().indexOf(meta[3].toLowerCase()) >= 0; }; Example: $("div:icontains('John')").css("text-decoration", "underline"); – Brandon Bloom Aug 06 '10 at 00:16
  • does this work for json? What if I want to filter some text in a json value? – Chamilyan Sep 25 '10 at 00:10
  • 5
    This won't work if text node has line break eg. "I am a simple\n string". Note that line break will not be rendered so user won't even know where is the problem. All these solutions have this problem. See http://jsfiddle.net/qPeAx/ – jesper Aug 13 '11 at 10:43
  • 9
    -1,doesn't work. It returns all parent nodes in the tree, you want only the lowest one. – Tomas Nov 29 '11 at 11:35
  • if i found a text m how can i append another word or character on the right handside of the found work? say, i am having a form , with firstname as text box. i wanna add a * , asterik , implies that, its mandatory. i wanna add this asterik using jQuery. how to achieve it – samolpp2 Apr 05 '19 at 07:32
51

Normally jQuery selectors do not search within the "text nodes" in the DOM. However if you use the .contents() function, text nodes will be included, then you can use the nodeType property to filter only the text nodes, and the nodeValue property to search the text string.

    $('*', 'body')
        .andSelf()
        .contents()
        .filter(function(){
            return this.nodeType === 3;
        })
        .filter(function(){
            // Only match when contains 'simple string' anywhere in the text
            return this.nodeValue.indexOf('simple string') != -1;
        })
        .each(function(){
            // Do something with this.nodeValue
        });
BarelyFitz
  • 1,967
  • 13
  • 17
  • 8
    To clarify, as Tony mentioned you can use the ":contains()" selector to search within the text nodes, but jQuery will not return the individual text nodes in the results (just a list of elements). But when you use .contents(), then jQuery returns both elements and text nodes. For more info: http://docs.jquery.com/Traversing/contents – BarelyFitz May 29 '09 at 16:31
  • 5
    I’m puzzled by why you are using `.andSelf()`. From http://api.jquery.com/andSelf/: "jQuery objects maintain an internal stack that keeps track of changes to the matched set of elements. When one of the DOM traversal methods is called, the new set of elements is pushed onto the stack. If the previous set of elements is desired as well, .andSelf() can help." I see no previous context, what am I missing? – Alan H. Jan 15 '11 at 01:41
  • 1
    Upvoted! Should be the accepted answer because it filters out all parent nodes which I think is the common need in most scenarios. – LucaM May 20 '16 at 07:25
33

This will select just the leaf elements that contain "I am a simple string".

$('*:contains("I am a simple string")').each(function(){
     if($(this).children().length < 1) 
          $(this).css("border","solid 2px red") });

Paste the following into the address bar to test it.

javascript: $('*:contains("I am a simple string")').each(function(){ if($(this).children().length < 1) $(this).css("border","solid 2px red") }); return false;

If you want to grab just "I am a simple string". First wrap the text in an element like so.

$('*:contains("I am a simple string")').each(function(){
     if($(this).children().length < 1) 
          $(this).html( 
               $(this).text().replace(
                    /"I am a simple string"/
                    ,'<span containsStringImLookingFor="true">"I am a simple string"</span>' 
               )  
           ) 
});

and then do this.

$('*[containsStringImLookingFor]').css("border","solid 2px red");
BlitZ
  • 12,038
  • 3
  • 49
  • 68
Slim
  • 5,635
  • 7
  • 31
  • 30
  • 3
    the .filter() function would be more useful than .each() here. In fact, you could actually put it in one selector: "*:contains('blah'):empty" – nickf May 29 '09 at 16:52
  • 2
    #nickf - :empty wont select a text node that contains text. So if the text node contains "I am a simple string" it is excluded. http://docs.jquery.com/Selectors/empty – Slim May 29 '09 at 17:14
  • 2
    I know I'm a little late here, but won't this ignore any text blocks where the string exists at the top level but there are children within it? e.g. :
    I am a simple string LIVING ON THE EDGE
    – Ken Bellows Mar 21 '12 at 13:22
  • I just want to mention that "containsStringImLookingFor" is not a valid HTML-Attribute. Please use for HTML 5 "data-containsStringImLookingFor". There is no way for HTML Version earlier than 5 to add custom Attributes... – Snickbrack Aug 26 '16 at 09:13
17

If you just want the node closest to the text you're searching for, you could use this:

$('*:contains("my text"):last');

This will even work if your HTML looks like this:

<p> blah blah <strong>my <em>text</em></strong></p>

Using the above selector will find the <strong> tag, since that's the last tag which contains that entire string.

nickf
  • 537,072
  • 198
  • 649
  • 721
  • 2
    This doesn't seem to work. On this page, for example, *:contains("the"):last returns only 1 element. – bzlm Mar 12 '10 at 14:53
  • 4
    @bzlm: well it didn't necessarily specify that he wanted to find multiple occurrences. – nickf Mar 14 '10 at 03:07
  • 2
    Aha. Is there a way to make it work for multiple occurrences? I think it's a more elegant solution than the ones involving recursive node type checking etc. – bzlm Mar 14 '10 at 10:15
  • 1
    the only way i could think to get the all the nodes, but not all the ancestors up the line would be to loop through the result, removing all parents, though you could have problems, eg: `

    my text my text

    ` -- removing the ancestors of the `` tag would lose the other match
    – nickf Mar 14 '10 at 15:01
14

Take a look at highlight (jQuery plugin).

Chris Doggett
  • 19,959
  • 4
  • 61
  • 86
  • 4 years on... My use case was that I needed to find any instance of ® (the registered trademark symbol - company name must have it) and superscript it. I've used this plugin to add the .hightlight class and CSS to halve the font-size and vertical-align:super. Your comment has made this possible. Thank you. – myshadowself Aug 31 '23 at 10:08
6

Just adding to Tony Miller's answer as this got me 90% towards what I was looking for but still didn't work. Adding .length > 0; to the end of his code got my script working.

 $(function() {
    var foundin = $('*:contains("I am a simple string")').length > 0;
 });
Pixelomo
  • 6,373
  • 4
  • 47
  • 57
2

this function should work. basically does a recursive lookup till we get a distinct list of leaf nodes.

function distinctNodes(search, element) {
    var d, e, ef;
    e = [];
    ef = [];

    if (element) {
        d = $(":contains(\""+ search + "\"):not(script)", element);
    }
    else {
            d = $(":contains(\""+ search + "\"):not(script)");
    }

    if (d.length == 1) {
            e.push(d[0]);
    }
    else {
        d.each(function () {
            var i, r = distinctNodes(search, this);
            if (r.length === 0) {
                e.push(this);
            }
            else {
                for (i = 0; i < r.length; ++i) {
                    e.push(r[i]);
                }
            }
        });
    }
    $.each(e, function () {
        for (var i = 0; i < ef.length; ++i) {
            if (this === ef[i]) return;
        }
        ef.push(this);
    });
    return ef;
}
danatcofo
  • 703
  • 8
  • 18