← Back to context

Comment by Klaster_1

19 hours ago

I used this approach before and it indeed works better than the 2010-style jQuery mess. A good fit for userscripts too, where the problem you attempt to solve is fairly limited and having dependencies, especially with a build steps, is a pain. Note that you don't need jQuery for this at all, unless you are somehow stuck with ancient browser support as a requirement - querySelector, addEventListener, innerHtml - the basic building blocks of the approach - have been available and stable for a long time.

Unfortunately, nowadays writing userscripts is much harder than it used to be. Most websites are using some sort of reactive FE framework so you need to make extensive use of mutationObservers (or whatever the equivalent is in jQuery I guess).

  • I'm not a frontend dev but I came up with this and use it a lot in my userscripts. It's not the most efficient (it can certainly be refactored to create a MutationObserver singleton and then have each call hook into that) but it works well enough for my needs and lets me basically use an old-school to dealing with reactive sites (so long as you are fine with using async):

        function awaitElement(selector) {
            return awaitPredicate(selector, _ => true);
        }
    
        function awaitPredicate(selector, predicate) {
            return new Promise((resolve, _reject) => {
                for (const el of document.querySelectorAll(selector)) {
                    if (predicate(el)) {
                        resolve(el);
                        return;
                    }
                }
    
                // Create a MutationObserver to listen for changes
                const observer = new MutationObserver((_mutations, obs) => {
                    // You could search just inside _mutations instead of the entire DOM.
                    // Efficiency will depend primarily on how precise your selector is.
                    for (const el of document.querySelectorAll(selector)) {
                        if (predicate(el)) {
                            resolve(el);
                            obs.disconnect(); // Don't forget to disconnect the observer!
                            break;
                        }
                    }
                });
    
                // Start observing the document
                observer.observe(document.documentElement, {
                    childList: true,
                    subtree: true,
                    attributes: false,
                    characterData: false,
                });
            });
        }

  • I often go for `setInterval` over `MutationObserver` because it works and I don't need instant reactivity and I don't have to think too much about it.

  • Very true. I guess that depends on what websites you find issues with? I just checked mine and all of those are quality of life improvements for fully server rendered sites like HN or phpBB forums.

    • Yeah, I mostly use it for QoL improvements but for work related things. So Jira, Bitbucket, GitHub, Linear etc. basically whatever my employer uses. Back in the early 2010s most of that software was fully server rendered. Nowadays it's pretty rare for that to be the case.

      I just try and get LLMs to do it for me because I'm lazy, and they like to use setInterval instead of mutationObservers and if it works, I just live with the inefficiency.

      1 reply →