Finding the DOM element with specific text and modify it

33,648

Solution 1

Now, there's always "recursively search through the children till you find the deepest child with the string" approach, which I want to avoid.

I want to search for an element in an unordered random list. Now, there's a "go through all the elements till you find what you're looking for approach", which I want to avoid.

Old-timer magno tape, record, listen, meditate.

Btw, see: Find and replace text with JavaScript on James Padolsey's github
(also hig blog articles explaining it)

Solution 2

Edit: Changed querySelectorAll to getElementsByTagName from RobG's suggestion.

You can use the getElementsByTagName function to grab all of the tags on the page. From there, you can check their children and see if they have any Text Nodes as children. If they do, you'd then look at their text and see if it matches what you need. Here is an example that will print out the text of every Text Node in your document with the console object:

var elms = document.getElementsByTagName("*"),
    len = elms.length;
for(var ii = 0; ii < len; ii++) {
    var myChildred = elms[ii].childNodes;
    len2 = myChildred.length;
    for (var jj = 0; jj < len2; jj++) {
        if(myChildred[jj].nodeType === 3) {
            console.log(myChildred[jj].nodeValue);

            // example on update a text node's value
            myChildred[jj].nodeValue = myChildred[jj].nodeValue.replace(/test/,"123");
        }
    }
}

To update a DOM element's text, simple update the nodeValue property of the Text Node.

Solution 3

Don't use innerHTML with a regular expression, it will almost certainly fail for non-trivial content. Also, there are still differences in how browsers generate it from the live DOM. Replacing the innerHTML will also remove any event listeners added as element properties (i.e. like element.onclick = fn).

It is best if you can have the string enclosed in an element with an attribute or property you can search on (id, class, etc.) but failing that, a search of text nodes is the best approach.

Edit

Attempting a general purpose text selection function for an HTML document may result in a very complex algorithm since the string could be part of a complex structure, e.g.:

<h1>Some <span class="foo"><em>s</em>pecial</span> heading</h1>

Searching for the string "special heading" is tricky as it is split over 2 elements. Wrapping it another element (say for highlighting) is also not trivial since the resulting DOM structure must be valid. For example, the text matching "some special" in the above could be wrapped in a span but not a div.

Any such function must be accompanied by documentation stating its limitations and most appropriate use.

Solution 4

Forget regular expressions.

Iterate over each text node (and doing it recursively will be the most elegant) and modify the text nodes if the text is found. If just looking for a string, you can use indexOf().

Share:
33,648
rk1s
Author by

rk1s

Updated on October 30, 2021

Comments

  • rk1s
    rk1s over 2 years

    I'm trying to figure out how to, in raw javascript (no jQuery, etc.), find an element with specific text and modify that text.

    My first incarnation of the solution... is less than adequate. What I did was basically:

    var x = document.body.innerHTML;
    x.replace(/regular-expression/,"text");
    document.body.innerHTML = x;
    

    Naively I thought I succeeded with flying colors, especially since it was so simple. So then I added an image to my example and thought I could check every 5 seconds (because this string may enter the DOM dynamically)... and the image flickered every 5 seconds.

    Oops.

    So, there has to be a correct way to do this. A way that specifically singles out a specific DOM element and updates the text portion of that DOM element.

    Now, there's always "recursively search through the children till you find the deepest child with the string" approach, which I want to avoid. And even then, I'm skeptical about "changing the innerHTML to something different" being the correct way to update a DOM element.

    So, what's the correct way to search through the DOM for a string? And what's the correct way to update a DOM element's text?

  • user3167101
    user3167101 almost 13 years
    Simple until they want to look for anything that will clobber the serialised HTML, a very real possibility.
  • Ibu
    Ibu almost 13 years
    yeah not so simple i guess when you are looking for a complex patterns
  • rk1s
    rk1s almost 13 years
    How do I iterate over each text node? I've never done this before.
  • rk1s
    rk1s almost 13 years
    Can't depend on it being in an enclosed element. I need it very versatile because I want people to be able to stick this code on any page. It's not for me, it's for our eventual customers.
  • rk1s
    rk1s almost 13 years
    I can't depend on it being anywhere specific. I need it versatile and dynamic because it needs to work on anyone's webpage.
  • user3167101
    user3167101 almost 13 years
    @rk1s This article seems to nail it.
  • gblazex
    gblazex almost 13 years
    +1 for lost mentioning event listeners. Most common ignored side effect of innerHTML.
  • RobG
    RobG almost 13 years
    There is no need to use qSA, which is only available in very recent browsers. getElementsByTagName('*') will do the same thing (more or less, it returns a live list rather than static, but the same elements are in both lists in the same order) and is supported by every browser in use.
  • rk1s
    rk1s almost 13 years
    I think that article did nail it. thanks :) just gotta try it out now
  • user3167101
    user3167101 almost 13 years
    +1 So event handlers attached with addEventListener() or attachEvent() will still work when serialising HTML and then resetting it? Never knew that... I always thought they all would break.
  • RobG
    RobG almost 13 years
    The search doesn't have to be recursive, it can iterative over a NodeList returned by getElementsByTagName.
  • user3167101
    user3167101 almost 13 years
    @RobG Sure. But I never said it had to be.
  • patorjk
    patorjk almost 13 years
    Good catch RobG, you're correct. That's a better choice then querySelectorAll in this instance.
  • Joe B.
    Joe B. about 7 years
    The blog mentions an updated link to some newer ideas: j11y.io/javascript/replacing-text-in-the-dom-solved The accompanying js works like a charm.
  • user85155
    user85155 over 2 years
    The link is dead, available via: archive.org web.archive.org/web/20161215111336/http://james.padolsey.com‌​/…
  • user85155
    user85155 over 2 years
  • gblazex
    gblazex over 2 years
    updated the links, thanks