3

I have gone through so many convoluted answers to this question and no one seems to be able to give a simple solution....

I typically use a setTimeout function to wait for all elements on a page to load before doing some work. My job requires me to wait for the angular app to load before I can manipulate things in the DOM. Using a setTimeout generally works but every now and again a customer is using a potato with a screen and when their CPU is as slow as a snail my DOM manipulation functions fail to execute in time.

The following block of code works for what I need it to do in 99% of instances but in this particular case the customer is reporting that it is not working on their end:

setTimeout(() => {
    const commentLabel = document.querySelectorAll('.donation-label');

    commentLabel.forEach((element) => {
        if (element.textContent.includes('Leave a comment')) {
            element.innerHTML = 'Tell us how to improve';
        }
    });

}, 750);

Instead of using a setTimeout where I am waiting for some duration of time to execute the "work", I want to use something that simply waits for the element with ".donation-label" (or any other selector of my choosing) to exist and then do something.

This needs to be in vanilla JS (no Jquery). I'm finding answers related to MutationObservers and async but can't find anything simple or anything that even works for that matter. Please help. Thank you very much!

2 Answers 2

2

After working with a colleague, we came up with this as the simplest and cleanest working version of what I was looking for....

function customize(_, observer) {

    let commentLabel = document.querySelectorAll('.comment-label');

    commentLabel.forEach((elm) => {
        if (elm.textContent.includes('Leave a comment')) {
            elm.innerHTML = 'Tell us how we can improve';
        }
    });

    if (commentLabel.length > 0 && observer) {
        observer.disconnect();
        console.log('observer disconnected');
    }
}

new MutationObserver(customize).observe(document, { childList: true, subtree: true });

Thank you all for your input!!

Sign up to request clarification or add additional context in comments.

Comments

0

You can use a "Mutation Observer" to watch your body (or any other target) to elements addings and removals. To watch any DOM target, you could write a watcher like this:

function watch(target, cb, options) {
      /** have sure that you have no duplicated observers **/

      if (window.__customChildListObserver) {
        return false;
      }

      /** observing our body and waiting for added components **/

      const observer = new MutationObserver((mutationList, observer) => {
        const changings = mutationList.reduce((acc, m) => {
          acc.push(...Array.from(m.addedNodes).map(node => ({ type: 'added', node })));
          acc.push(...Array.from(m.removedNodes).map(node => ({ type: 'removed', node })));

          return acc;
        }, []);

        changings.forEach(change => (cb && cb(change)));
      });

      /** start to watch the target, we want to observe only childlist and subtree **/
      observer.observe(target, options || { attributes: false, childList: true, subtree: true  });

      /** informs the window of our observer **/
      window.__customChildListObserver = observer;
    }

And you can use your watcher to wait for a .donation-label like this:

    /** 
     * everytime body receives a node, you will be informed with the mutation 
     * {type: 'added', node: p}
     * 
     * everytime body has a node removed, you be informed with the mutation
     * {type: 'removed', node: button}
     */
    watch(document.body, (mutation) => {
      if (mutation.type === 'added' && mutation.node.classList.contains('.donation-label')) {
        /** .donation-label has been attached somewhere in the body **/
        const element = mutation.node;
        /** do whatever you want with the .donation-label element **/
      }
    });

If you have to take in account that .donation-label can be in the DOM before you have setted you watcher, you can write a function to search for this element before start the watcher, than you will be sure that you will always grab the desired element.

Reference about the Mutation Observer: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.