首页 > 解决方案 > 在点击事件上停止 MutationOberserver

问题描述

我有一个动态 DOM 结构,其中节点一直被插入和删除,为了对新节点做出反应,我正在使用 MutationObserver,当用户单击按钮时它会被激活。每次用户单击该按钮时,MutationObserver 都会运行两个函数,并且当我观察到的 DOM 结构发生变化时,这些函数将继续运行。

我要做的是在用户再次单击按钮后立即断开 MutationObservers(假设第一次单击然后对元素进行排序,再次单击并停止对元素进行排序)。每次用户单击按钮时,我都会传递一个布尔值(goSortDom)(如果为 true,则执行操作并观察,如果为 false,则停止观察)。问题是当我尝试在 goSortDom=false 时断开观察者的连接时,它们将永远不会断开连接并且函数将继续运行。

我对 mutationObservers 很陌生,所以我不知道我的方法是否是一种不好的做法,这就是为什么我也会非常感谢对此的评论。

$(document).ready(function () {
  // Function 1: Assign Priorities
  function assignChatPriority(arrayParent) {
     //Assign Priorities
     //Observe if time change, if change re-assign priority to dom node
     timeInQueueDomObserver.observe(
     $(incomingChat).find(".time_cell")[0],
     configtimeInQueue
  }

  // Function 2: Reorder DOM
  function orderIncomingChats() {
    //Reorder DOM save DOM elements to Array
    //Replace original DOM with new sorted DOM 
  }

  //Declaring DOM Observers for Time in Queue and DOM container dynamics
  var incomingChatsDomObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
      if (mutation.addedNodes.length) {
        for (let i = 0; i < mutation.addedNodes.length; i++) {
          if (
            mutation.addedNodes[i].attributes["data-priority"] === undefined
          ) {
            runExtensionFunctions();
          }
        }
      }
    });
  });
  var timeInQueueDomObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
      if (
        mutation.addedNodes.length === 1 &&
        mutation.addedNodes[0].nodeType === Node.TEXT_NODE &&
        mutation.removedNodes.length === 1 &&
        mutation.removedNodes[0].nodeType === Node.TEXT_NODE
      ) {
        runExtensionFunctions();
      }
    });
  });

  //Obervers Configuration
  let configChatsContainer = {
    childList: true,
  };
  let configtimeInQueue = {
    childList: true,
  };
  if (goSortDom) {
    //boolean is true go ahead run functions and observe
    assignChatPriority(incomingChatsRows);
    orderIncomingChats();
    incomingChatsDomObserver.observe(
      incomingChatsContainer[0],
      configChatsContainer
    );
  } else {
    //disconnect observers: WON'T WORK
    incomingChatsDomObserver.disconnect();
    timeInQueueDomObserver.disconnect();
  }
}

标签: jquerygoogle-chrome-extensionmutation-observers

解决方案


您初始化并处理 MutationObserver incomingChatsDomObserver,并timeInQueueDomObserver在此函数中传递给 .ready()。我没有看到外部参考。如果是这种情况,if (goSortDom)则只会对初始化的 MutationObserver 进行一次评估。之后,函数结束,引用丢失。再次调用该函数会创建的 MutationObserver,并且 disconnect() 调用将引用那些新的 MutationObserver 而不是已经观察到的旧的!

难道你想要实现的是将MutationObserver的初始化和引用移到这个函数之外并在那里使用它?或者可能断开自己内部的 MutationObservers?

让我试一试。由于这不是完整的代码,我无法自己测试:

  1. 想法(移动参考):
// ANSWERER: Now the references are outside the ready function. If the ready function is done, this references still exist. 
  //Declaring DOM Observers for Time in Queue and DOM container dynamics
  var incomingChatsDomObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
      if (mutation.addedNodes.length) {
        for (let i = 0; i < mutation.addedNodes.length; i++) {
          if (
            mutation.addedNodes[i].attributes["data-priority"] === undefined
          ) {
            runExtensionFunctions();
          }
        }
      }
    });
  });
  var timeInQueueDomObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
      if (
        mutation.addedNodes.length === 1 &&
        mutation.addedNodes[0].nodeType === Node.TEXT_NODE &&
        mutation.removedNodes.length === 1 &&
        mutation.removedNodes[0].nodeType === Node.TEXT_NODE
      ) {
        runExtensionFunctions();
      }
    });
  });

// ANSWERER: This is now possible  
function disconnectObservers() {
    incomingChatsDomObserver.disconnect();
    timeInQueueDomObserver.disconnect();
}

$(document).ready(function () {
  // Function 1: Assign Priorities
  function assignChatPriority(arrayParent) {
     //Assign Priorities
     //Observe if time change, if change re-assign priority to dom node
     timeInQueueDomObserver.observe(
     $(incomingChat).find(".time_cell")[0],
     configtimeInQueue
  }

  // Function 2: Reorder DOM
  function orderIncomingChats() {
    //Reorder DOM save DOM elements to Array
    //Replace original DOM with new sorted DOM 
  }


  //Obervers Configuration
  let configChatsContainer = {
    childList: true,
  };
  let configtimeInQueue = {
    childList: true,
  };
  if (goSortDom) {
    //boolean is true go ahead run functions and observe
    assignChatPriority(incomingChatsRows);
    orderIncomingChats();
    incomingChatsDomObserver.observe(
      incomingChatsContainer[0],
      configChatsContainer
    );
  } else {
    //disconnect observers: WON'T WORK
    incomingChatsDomObserver.disconnect();
    timeInQueueDomObserver.disconnect();
  }
}

  1. 版本。MutationObserver 的断开连接:
$(document).ready(function () {
  // Function 1: Assign Priorities
  function assignChatPriority(arrayParent) {
     //Assign Priorities
     //Observe if time change, if change re-assign priority to dom node
     timeInQueueDomObserver.observe(
     $(incomingChat).find(".time_cell")[0],
     configtimeInQueue
  }

  // Function 2: Reorder DOM
  function orderIncomingChats() {
    //Reorder DOM save DOM elements to Array
    //Replace original DOM with new sorted DOM 
  }

  //Declaring DOM Observers for Time in Queue and DOM container dynamics
  var incomingChatsDomObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
      if (mutation.addedNodes.length) {
        for (let i = 0; i < mutation.addedNodes.length; i++) {
          if (
            mutation.addedNodes[i].attributes["data-priority"] === undefined
          ) {
            runExtensionFunctions();
          }
        }
      }
    });
    // ANSWERER: Now the observer disconnects itself after the run. Remember: MutationObserver's disconnect itself from *everywhere*
    incomingChatsDomObserver.disconnect();
  });
  var timeInQueueDomObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
      if (
        mutation.addedNodes.length === 1 &&
        mutation.addedNodes[0].nodeType === Node.TEXT_NODE &&
        mutation.removedNodes.length === 1 &&
        mutation.removedNodes[0].nodeType === Node.TEXT_NODE
      ) {
        runExtensionFunctions();
      }
    });
    // ANSWERER: Now the observer disconnects itself after the run. Remember: MutationObserver's disconnect itself from *everywhere*
    timeInQueueDomObserver.disconnect();
  });

  //Obervers Configuration
  let configChatsContainer = {
    childList: true,
  };
  let configtimeInQueue = {
    childList: true,
  };
  if (goSortDom) {
    //boolean is true go ahead run functions and observe
    assignChatPriority(incomingChatsRows);
    orderIncomingChats();
    incomingChatsDomObserver.observe(
      incomingChatsContainer[0],
      configChatsContainer
    );
  } else {
    //disconnect observers: WON'T WORK
    incomingChatsDomObserver.disconnect();
    timeInQueueDomObserver.disconnect();
  }
}

或者,请查看以下内容。MutationObserver 绑定到按钮对象,因此单击按钮总是引用曾经初始化的 MutationObserver。这样它们就可以断开连接并重新连接(尝试在浏览器中更改 div#changes):

<html>
<body>
    <button id="somebutton">bla</button>
    <div id="changes"></div>
    <script>
        document.querySelector("#somebutton").addEventListener("click", function () {
            if (!this.incomingChatsDomObserver) {
                this.incomingChatsDomObserver = new MutationObserver(function (mutations) {
                    console.log("incomingChatsDomObserver", mutations);
                });
            }

            if (!this.timeInQueueDomObserver) {
                this.timeInQueueDomObserver = new MutationObserver(function (mutations) {
                    console.log("timeInQueueDomObserver", mutations);
                });
            }

            if (this.observing) {
                this.observing = false;
                this.incomingChatsDomObserver.disconnect();
                this.timeInQueueDomObserver.disconnect();
                this.innerText = "inactive";
            } else {
                this.observing = true;
                const changingDiv = document.querySelector("#changes");
                this.incomingChatsDomObserver.observe(changingDiv, { attributes: true });
                this.timeInQueueDomObserver.observe(changingDiv, { attributes: true });
                this.innerText = "active";
            }
        });
    </script>
</body>
</html>

推荐阅读