I am writing some jquery script that will run across a large amount of completely different web pages (different domains too) and it will surround certain words on the page with html tags to style them up - make them more noticeable etc. Here's some sample code I've written...

$('body').ready(function() {
var body = $('body').html();
var matches = body.html().match(/a complex regular expression here which matches a variety of things/g);
var match;

for (var i=0; i<matches.length; i++)
  {
    match = matches.pop(); // go through each matched instance
    body = body.replace(match,"<h1>"+match+"</h1>"); // style each matched instance in the document body
  }

$('body').html(body); // set it back onto document body
});

The problem is, certain domains on which this script is to be run on - on 'body'.ready they only load very basic stuff and load nearly all their content afterwards using ajax/javascript.

So at line 2 of my code where I retrieve the body's html it only contains the bare bones for some of these webpages at this time therefore later when the matched instances are replaced and put onto the DOM the changes are made but on a page which was only half-loaded.

I hope this makes sense. If you know a workaround for this then please share.

Dani AI

Generated

A few practical points and a robust approach you can use across pages that load content after the initial DOM:

The root problem with the original approach is twofold: taking the whole body.innerHTML and doing global string replaces will clobber scripts, inline handlers and any live state on the page; and many sites inject HTML after DOMContentLoaded (via fetch/XHR/frameworks), so an initial one-time scan misses later content. ’s snippet is vulnerable to both issues. and correctly noted jQuery-level hooks and polling as options, and / pointed at event delegation — those help when the site uses jQuery and you control timing, but they don’t cover non-jQuery fetch/XHR insertions or avoid destroying event handlers.

A safer, more portable strategy:

  • Never replace the whole body.innerHTML. Instead scan text nodes and wrap matches in elements (so existing nodes and listeners stay intact).
  • Use a MutationObserver to catch content added after the initial load; run a single, safe text-node replacer on new nodes.
  • Prevent re-processing (mark nodes or briefly disconnect the observer while you modify DOM), and skip SCRIPT/STYLE/INPUT/TEXTAREA nodes for performance.

Example pattern (vanilla JS; adapt your regex/wrapping as needed):

// safeTextReplace: finds matches in text nodes under `root` and wraps them
function safeTextReplace(root, regex, wrapClass) {
  var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
  var textNodes = [];
  while (walker.nextNode()) textNodes.push(walker.currentNode);
  textNodes.forEach(function(t) {
    if (!t.nodeValue.trim()) return;
    var m, last=0, frag=document.createDocumentFragment();
    regex.lastIndex = 0;
    while ((m = regex.exec(t.nodeValue)) !== null) {
      if (m.index > last) frag.appendChild(document.createTextNode(t.nodeValue.slice(last, m.index)));
      var el = document.createElement('span');
      el.className = wrapClass;
      el.textContent = m[0];
      frag.appendChild(el);
      last = m.index + m[0].length;
      if (!regex.global) break;
    }
    if (last) frag.appendChild(document.createTextNode(t.nodeValue.slice(last)));
    if (frag.childNodes.length) t.parentNode.replaceChild(frag, t);
  });
}

Hook it up with a MutationObserver:

var rx = /yourComplexRegex/g;
safeTextReplace(document.body, rx, 'my-highlight'); // initial pass

var obs = new MutationObserver(function(muts) {
  obs.disconnect();
  muts.forEach(function(m) {
    m.addedNodes.forEach(function(n) {
      if (n.nodeType === 3) safeTextReplace(n.parentNode, rx, 'my-highlight');
      else if (n.nodeType === 1) safeTextReplace(n, rx, 'my-highlight');
    });
  });
  obs.observe(document.body, { childList: true, subtree: true });
});
obs.observe(document.body, { childList: true, subtree: true });

Caveats and tips: test performance with large pages and complex regexes; prefer a scoped root (not the whole body) where possible; avoid repeating work by checking for your wrapper class before processing; for very old browsers fall back to polling. This approach preserves event handlers, works regardless of whether content came from jQuery/fetch/XHR or hard-coded HTML, and avoids the pitfalls discussed earlier in the thread.

Recommended Answers

All 7 Replies

pixelsoul, besides live() being deprecated, there is no event that you can use with it to know when an content has been changed.

The change() event is only for inputs, textareas and selects.
The load() event is only for window, img, scripts and frames.

So I don't see any use of live() is this operation.

But maybe I'm forgetting something, who knows =P

Ah... you're right. I didn't even see that it had been deprecated. It looks like they moved to .on()

I only mentioned it because I remember a time I was watching someone build a UI that was mostly ajax calls, and I thought I remembered them saying something about a way to affect the content that gets rendered after the page has loaded. I thought it may have been .live() but it was quite a while ago.

There's .on(), and then there's also .one() when you only want it to execute once and then unbind itself after.

I'm going to have to play around with .on() later and tests the behavior on asynch stuff.

Replace your $('body').ready( with $(document).ajaxStop(

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.