Run a function once in MutationObserver for loop

70 Views Asked by At

I'm using MutationObserver to observe if any element with the class last-move changes its inline style. For example, it could look like this: <square class="last-move" style="transform: translate(204px, 68px);"></square> On the page, there are two squares with this last-move class, so I need to observe if any of them change. If either one or both of them change, I'd like to run the function pieceMoved(). This is the foundation I'm working with.

function observeNewMove() {
  const targetElements = document.querySelectorAll('.last-move');

  targetElements.forEach((targetElement) => {
    const observer = new MutationObserver((mutationsList, observer) => {
      for (const mutation of mutationsList) { // For loop to see if any of the two elements have changed.
        if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
            pieceMoved(); // Function to be called once.
        }
      }
    });

    observer.observe(targetElement, { attributes: true });
  });
}

The problem becomes that pieceMoved() is called at least twice since the two squares with last-move class both change style most of the times, but I'd like for pieceMoved() to be called once, even if the for loop runs for two iterations.

To sum it up, I'd like for pieceMoved() to be run only once, even if the for loop iterates for more than that.

I've tried using different type of flag systems with both local and global variables, starting with false-true, then true-false, and everything in-between. Here's an example

function observeNewMove() {
  let flag = false;
  const targetElements = document.querySelectorAll('.last-move');

  targetElements.forEach((targetElement) => {
    const observer = new MutationObserver((mutationsList, observer) => {
      for (const mutation of mutationsList) { // For loop to see if any of the two elements have changed.
        if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
            if (flag) {
                pieceMoved(); // Function to be called once.
            }
            flag = true;
        }
      }
    });

    observer.observe(targetElement, { attributes: true });
  });
}

This will work for the first time observeNewMove() is called, but not for subsequent moves (as observeNewMove() is not called again, it kind of runs forever until disconnected if I've understood it correctly). Changing the placement of let flag = false; will either cause pieceMoved() to never be called (flag = false always, or called twice all the time (flag = true always).

1

There are 1 best solutions below

1
Konrad On
function observeNewMove() {
  const targetElements = document.querySelectorAll('.last-move');

  targetElements.forEach((targetElement) => {
    const observer = new MutationObserver((mutationsList, observer) => {
      // declare it before loop
      let once = false
      for (const mutation of mutationsList) { // For loop to see if any of the two elements have changed.
        if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
            // change it in the loop
            once = true
        }
      }
      // check it after the loop
      if (once) {
        pieceMoved(); // Function to be called once.
      }
    });

    observer.observe(targetElement, { attributes: true });
  });
}