The MutationObserver at the end of the rabbit hole

Cover Image for The MutationObserver at the end of the rabbit hole

We have an internal news feed like system with various other features that's crucial to our companies way of life. Think Facebook but for the working class.

The specific pain point was, that one specific text-box that I'd like to have resizable was not, which was an issue when writing larger posts. I started toying with the idea of doing a small CSS alterations via a Chrome extension called Stylish since it seemed super simple, just modify the CSS right? Wrong. Stylish worked, but wasn't able to solve my problem due to the following issues:

One of the quirks of this application is that it's written in GWT utilising a "portlet" style system. In terms of "code" being delivered to the browser we have the following flow:

  1. all primary dependent code paths and libs get pushed down, top level CSS and JS, etc..
  2. after that, the rest of the DOM gets constructed, via GWT deciphering its payload
  3. and then lazy loading sub sections of the application

Because of the lazy loading, Stylish wasn't able to alter the CSS before it being rendered by the browser (as the CSS I was after, is deep down the tree structure).

Take 2.

A colleague then noticed what I was doing, suggested that we try another approach, and asked if we can team up to do some other smaller tweaks as well while solving the above (He wanted additional markdown support). We then decided upon TamperMonkey, as it was clear we needed some JS to do heavy lifting.

We then hit a similar issue with the JavaScript due to the lazy loading... and then I remembered the following. It's all DOM mutations :D. Such a simple solution to this problem.

Checkout the docs: Mutation Observer.

And below is an extract of my script. Observe a section of the page, if we find the

var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

// select the target parent node to monitor. 
var target = document.querySelector('.page-column-2');

// I noticed the app already injected [Showdown](https://github.com/showdownjs/showdown)
// ...but not utilising it.

// lets create a new instance of the parser and see what we can get up to
var showdown = new Markdown.Converter().makeHtml;                  

// create an observer instance
var observer = new MutationObserver(function(mutations, observer) {
    mutations.forEach(function(mutation) {

        // find a message item inside the news feed
        if(mutation.target.className == 'message-container') {
            // get the inner nested GWT wrapper object
            if(mutation.addedNodes[0] && mutation.addedNodes[0].className == 'gwt-HTML') {
                // markdown :D 
                mutation.addedNodes[0].innerHTML = showdown(mutation.addedNodes[0].innerHTML);
            }
        }

if(mutation.target.className == 'new-post-box') {
// alter element style props
}
    });
});

// pass in the target node, as well as the observer options
observer.observe(target, { childList: true, subtree: true });

// at this stage, we'd normally disconnect the observer,
// but as the feed gets refreshed. we need to keep watching.


More Stories

Cover Image for Guitar Heroes: Monuments

Guitar Heroes: Monuments

Holy hell. What and experience!

Cover Image for Hello 3.0 World

Hello 3.0 World

Hello 3.0 World