DEV Community

Pascal Louwes
Pascal Louwes

Posted on • Originally published at on

My Secret to Super Fast AB Test Loading

When I am testing elements that take some time to load, the last thing I want is a flash of un-styled content, or see the unchanged element jump into its changed state. Since a few years, browsers have a great API built in that I use to achieve super fast loading of my test code: MutationObserver. (link opens new tab)

In this post I’ll explain how I use this API to my advantage.

Make sure your script is loaded as soon as possible. It’s OK if you load it asynchronously, but you want it to be available as the first piece of JS the page is loading.

Here’s the function I use to observe when an element gets added to the DOM. I basically wrap a querySelector in a MutationObserver. The latter will fire upon every DOM mutation. The querySelector will then test for the existence of my element. If that returns true, I disconnect the observer if I don’t need it anymore. Finally, I run the callback function that was passed as the second parameter.

const observeDOM = (config, callback) => {
    // use a regular function so `this` refers to the Mutation Observer
    new MutationObserver(function() {
        const element = document.querySelector(config.selector);
        if (element) {
            // disconnect if you don't need it again
            if(config.disconnect) this.disconnect();
            // test and run the callback function
            if(typeof callback === "function") callback(element);
    }).observe(config.parent || document, {
        subtree: config.recursive,
        childList: true
Enter fullscreen mode Exit fullscreen mode

I use a ‘normal’ function keyword on the Mutation Observer function because if I don’t, I won’t be able to disconnect it if that is what I want. This will then refer to the Window object and not the MutationObserver instance.

const config = {
    selector: 'li.timeline-item', // what element are we looking for?
    parent: document.querySelector("ul.timeline"), // narrow down search scope if possible...
    recursive: true, // look at descendant elements too
    disconnect: false // disconnect when one hit is enough
Enter fullscreen mode Exit fullscreen mode

In the config file above you can see that I am observing an unordered list for additions of list items. Since disconnect is set to false, the observer will fire on every mutation and do the element test again. Note: You can prevent triggering on the same element over and over again by adding a class (.found) to the element as soon as it’s found, and change your selector accordingly: a li.timeline-item:not(.found) selector does that trick just fine.

// run this function when the element is found
const callback = console.log;
Enter fullscreen mode Exit fullscreen mode

Here is a simple example of a callback function you can run when you have a hit. In your case you probably want to kick off your AB test code. See what I did there?

// kickoff mutation observer
observeDOM(config, callback);
Enter fullscreen mode Exit fullscreen mode

Last but not least, you want to start observing by calling your function with config and callback parameters.

Happy coding!

The post My Secret to Super Fast AB Test Loading appeared first on recoveryArea.

Discussion (0)