Currently reading: The Runaway Jury by John Grisham

A simpler alternative to the handy MutationObserver

Don't get me wrong: MutationObserver is a great addition to JS, but I find its API a bit clumsy and verbose. I always forget the specifics of its parameters and have to go look them up. Sometimes you just want to write simpler code without needing a reference, you know? Whenever I feel like I have a MutationObserver problem brewing, I go "Dang it!" and consider my options.

One of those options is pulling arrive.js into the mix. It's a simpler syntax that's easier to remember for the basic task of watching for particular elements' arrival. I mostly end up feeling it's not worth it to bring an entire separate library into play for a simple change, so I then consider another MutationObserver-avoiding option: Manually watching for the arrival of elements by querying the DOM for one of them in a loop.

Say you have some third-party JS running that's going to add spans to a code element. You want to wait until this has happened so you can modify that code block without interfering. You could write an observer to handle it, making it watch code elements for spans appearing in their subtrees, but this simpler, almost dumb, loop accomplishes the same thing in the end.

// ≈≈ observer.observe()
const spanWatch = setInterval(() => {
    if (document.querySelector('code span')) {
        clearInterval(spanWatch);
        // Do your thing here.
    }
}, 100); // Not too fast!

Ten times a second, which ain't particularly frequent on the scale of CPUs, it asks the document if it's got any code span matches. We don't need querySelectorAll() because we only want to know that one now exists on the page. Since finding one is sufficient, I assume calling querySelector() instead is a simple and efficient operation. As soon as one span is found, we stop the interval loop and go about our business.

I haven't profiled the comparative efficiencies of this 10-Hz querySelector loop versus MutationObserver. There's certainly some frequency where it's not worth it to query the DOM and you might as well give in and observe it properly. My first instinct is to say MutationObserver is less efficient since it's this whole thing that has to be spun up, but it may be tied so deeply to the core DOM systems that it's effectively free to run on top. Maybe I'll test it one day and update this post with my findings.

Efficiency aside, an observer is much faster to respond to changes, so definitely use one if speed is important. Here we've imposed a strict limit on our document-check frequency that will cause a brief but noticeable lag before any attached stuff happens. But if what you're doing isn't important to the page's functionality or UX, if it's just some little adjustments here and there for aesthetics, you don't really need instant reactions from an observer, do you?

More important is that I ⸺ I mean, you ⸺ didn't have to open up that MDN page for the hundredth time to make sure you've got the setup and syntax correct for MutationObserver. What a bother!

Leave a comment

Your email address will not be published. Required fields are marked *