【问题标题】:Stop MutationOberserver on click event在点击事件上停止 MutationOberserver
【发布时间】:2020-08-19 10:30:02
【问题描述】:

我有一个动态 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();
  }
}

【问题讨论】:

    标签: jquery google-chrome-extension mutation-observers


    【解决方案1】:

    您在传递给 .ready() 的此函数中初始化并处理 MutationObserver 的 incomingChatsDomObservertimeInQueueDomObserver。我没有看到外部参考。如果是这种情况,if (goSortDom) 将只为初始化的 MutationObserver 评估一次。之后,函数结束,引用丢失。再次调用该函数会创建 new 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>
    

    【讨论】:

    • 感谢您的回答@Benno。第一个选项不起作用,因为突变观察者调用了一个必须在文档就绪函数中声明的函数(长话短说,我必须等待整个 DOM 加载然后在该函数中执行操作)。第二个选项也不起作用。我真的不知道为什么,但是当一个新的 dom 元素进入结构时,assignChatPriority 中的 DOM 就会发生变化。您的第三个建议让我感到困惑,抱歉。
    • 全部归结为:如果您的 sn-p 是真实代码,则整个 .ready() 函数将只运行一次,这意味着 if(goSortDom) 只运行一次,而 MutationObserver 的对象只初始化一次。让我们关注incomingChatsDomObserver。它被初始化一次,然后要么用.observe().disconnect()调用。之后,引用丢失,因此它永远不会观察它是否断开连接,或者如果它观察则断开连接。我很确定你的问题归结为这个。
    • 谢谢@Benno,您的回答和深入的解释对我帮助很大。
    【解决方案2】:

    您的代码似乎无法识别 (goSortDom) 点击事件。您是否尝试过将该代码字符串添加到您的按钮单击事件中? Vue.js 框架可能非常适合您的要求。除此之外,我能告诉你的就是你知道的同样的事情。当 dom 元素动态更改时,Javascript 的行为非常奇怪。我有一个应用程序,该应用程序具有基于先前选择加载的指数数量的选择。每次加载新信息时,我都必须特别注意用于操作数据的选择器。否则,无论是 Id 还是类选择器,JS 都无法处理新导入的数据。我还必须使用 .on( events [, selector ] [, data ] ) Javascript 函数而不是 .click() jquery 函数来完成工作。特别确保选择器信息已填写。
    $("#myDiv").on('click', '.MyDivClass', function(){'Select All and return information'};

    【讨论】:

    • 感谢马特,但不幸的是 Vue 不是一个选项,因为我正在使用一个已经开发的平台。除此之外,我很确定仍然有一种方法可以仅使用 javascript / JQuery 来完成我正在寻找的东西,但正如我之前所说,我是 mutationObservers 的新手,所以仍然试图完全弄清楚它们。
    猜你喜欢
    • 2016-03-29
    • 1970-01-01
    • 1970-01-01
    • 2014-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多