如果 DOM 发生变化,我找不到重新加载事件侦听器的好方法

I can't find a good method to reload event listener if the DOM changes

提问人:Jeje Pro 提问时间:10/22/2023 更新时间:10/22/2023 访问量:35

问:

我创建了一个 MouseContext,目标是在鼠标悬停时播放自定义动画。

如果可能的话,我希望只使用 useRef 来做到这一点,但我发现在我的应用程序的整个生命周期内必须将 ref 放在任何需要悬停操作的元素上是一种耻辱。

所以我尝试使用这样的手动事件侦听器:

  useEffect(() => {
      function inOutHandler(event) {
        event.preventDefault();
        const cursorType = event.type === "mouseenter" ? "active" : "";
        if (
          window.getComputedStyle(event.target)["cursor"].split(", ")[1] ===
          "pointer"
        )
          mouseHandler({ type: "mouseHover", cursorType: cursorType });

        window.removeEventListener("mouseenter", inOutHandler);
        window.removeEventListener("mouseout", inOutHandler);
      }

      const links = document.querySelectorAll(
        "a, .button, button"
      );
      for (let link of links) {
        link.addEventListener("mouseenter", inOutHandler);
        link.addEventListener("mouseout", inOutHandler);
      }
  }, []);

好吧,听起来不错,但是当一个新元素被添加到 DOM 中时,我必须再听一遍他的声音,处理起来很混乱。

再一次,我尝试了一些东西,用 MutationObserver :

  const mutationObserver = new MutationObserver(async (mutationList, obs) => {
    if (mutationList[0].addedNodes) {
      function inOutHandler(event) {
        event.preventDefault();
        const cursorType = event.type === "mouseenter" ? "active" : "";
        if (
          window.getComputedStyle(event.target)["cursor"].split(", ")[1] ===
          "pointer"
        )
          mouseHandler({ type: "mouseHover", cursorType: cursorType });

        window.removeEventListener("mouseenter", inOutHandler);
        window.removeEventListener("mouseout", inOutHandler);
      }

      const links = document.querySelectorAll(
        "a, .admin .button, button"
      );
      for (let link of links) {
        link.addEventListener("mouseenter", inOutHandler);
        link.addEventListener("mouseout", inOutHandler);
      }
    }
  });

  useEffect(() => {
    if (mainRef.current) {
      mutationObserver.observe(mainRef.current, {
        childList: true,
        subtree: true,
      });

      return () => {
        mutationObserver.disconnect();
      };
    }
  }, [mutationObserver]);

这就是工作!但: 突变观察者也在监听从 dom 中消失的元素,因此,每次更改幻灯片(例如),我的监听器都会执行两次。这在性能方面给我带来了问题。

你们有什么建议给我吗?

ReactJS 表达 突变观察者

评论

0赞 jfriend00 10/22/2023
听起来您可能希望使用事件传播,在公共父级而不是实际的 DOM 元素上侦听事件。这样,您可以在父级上安装一次侦听器,并自动查看所有子事件,即使 DOM 元素来来去去(只要您正在侦听的父级不更改)。

答:

0赞 Jeje Pro 10/22/2023 #1

感谢 jfriend00 :

听起来您可能希望在侦听的地方使用事件传播 对于公共父级上的事件,而不是实际的 DOM 元素上的事件。 这样,您可以在父级上安装一次侦听器并自动安装 查看所有子事件,即使 DOM 元素来来去去(只要 您正在收听的父级不会更改)。

我不知道父监听器会处理 DOM 更改。 而且因为我检查当前元素 event.target 是否设置为指针,这仅适用于链接和按钮,谢谢!

在性能方面,这需要绝对测试应用程序的所有元素,我不知道这是否非常有影响力。

新代码:

  function inOutHandler(event) {
    const cursorType = event.type === "mouseenter" ? "active" : "";
    if (
      window.getComputedStyle(event.target)["cursor"].split(", ")[1] ===
      "pointer"
    )
      mouseHandler({ type: "mouseHover", cursorType: cursorType });

    mainRef.current.removeEventListener("mouseenter", inOutHandler);
    mainRef.current.removeEventListener("mouseout", inOutHandler);
  }

  useEffect(() => {
    if (!loader) {
      mainRef.current.addEventListener("mouseenter", inOutHandler, true); // don't forget the true
      mainRef.current.addEventListener("mouseout", inOutHandler, true);
    }
  }, [loader]);