【问题标题】:Propagation of custom events自定义事件的传播
【发布时间】:2016-12-22 16:22:15
【问题描述】:

我希望我的 CustomEvent 能够从文档传播到所有 DOM 元素。 由于某种原因,它不会发生。我做错了什么?

<html>
<script>
function onLoad() {
  var myDiv = document.getElementById("myDiv");
  myDiv.addEventListener("myEvent",function(){alert("Yes!");});
  document.dispatchEvent(new CustomEvent("myEvent",{detail:null}));
}
</script>
<body onload="onLoad()">
<div id="myDiv"></div>
</body>
</html>

【问题讨论】:

  • 是的,但我不想直接这样做。在我的真实代码中,DOM 结构是动态的,元素来来去去,所以我希望事件处理程序仅在其元素附加到 DOM 时才处于活动状态。
  • 可能是一个冒泡的事情。你试过myDiv.addEventListener("myEvent",function(){alert("Yes!"), true});
  • 是的,我做到了,无论是真的还是假的。没用。
  • 这是一个完整且可验证的示例。如果事件被传播,您应该看到“是!”警报。只需复制代码并在浏览器中运行即可。
  • @Rediska 我提供的小提琴修复了你的代码来做到这一点。如果这是minimal reproducible example,那么答案就是正确的。您完整的示例表明您知道要在哪个元素上触发事件。如果不是,那么我给出的关于事件传播如何工作的解释将表明,鉴于您提供的信息,不可能做您想做的事情。

标签: javascript html dom event-propagation custom-event


【解决方案1】:

默认情况下自定义事件不会冒泡。

原因是,自定义事件定义说:

让 event = new CustomEvent(type,[,options]).

选项有一个标志:bubbles,默认为false,我们必须启用它。

以下将修复您的代码

<html>
<script>
function onLoad() {
  var myDiv = document.getElementById("myDiv");
  myDiv.addEventListener("myEvent",function(){alert("Yes!");});
  document.dispatchEvent(new CustomEvent("myEvent",{detail:null,bubbles:true}));
}
</script>
<body onload="onLoad()">
<div id="myDiv"></div>
</body>
</html>

【讨论】:

  • 感谢您的回答,尽管这个问题已经有 5 年的历史了。
【解决方案2】:

您必须在要“触发”事件的元素上 dispatchEvent 事件。然后它将向下传播并备份 DOM

<html>
<script>
function onLoad() {
  var myDiv = document.getElementById("myDiv");
  myDiv.addEventListener("myEvent",function(){alert("Yes!");});
  myDiv.dispatchEvent(new CustomEvent("myEvent",{detail:null}));
}
</script>
<body onload="onLoad()">
<div id="myDiv"></div>
</body>
</html>

小提琴:https://jsfiddle.net/yrf9cvr3/

传播的工作原理:事件从document 开始,一直到它被调度的元素(您可以使用onCapture 标志捕获以这种方式进行的事件)。然后回到document。因为您在 document 上进行调度,所以该事件并没有真正去任何地方,只有 document 看到该事件。

【讨论】:

  • 嗯,重点是我不知道哪些元素需要事件。元素本身“知道”他们需要什么并相应地定义事件处理程序。
  • 我担心,当从 DOM 中移除元素时,这会导致内存泄漏。
  • @TheSharpieOne 使用document.querySelector() 指定要跟踪的节点或整个文档。然后,如果您有动态创建的页面,那就没关系了。插入了什么内容。您可以从中检索到很多东西。它会自动将事件传递给您要运行的任何功能。看看之前的帖子I have given这个解释
  • 使用 document.querySelector() 并不比在 DOM 的每个元素上调用 dispatchEvent 更好。我有点希望这个功能已经到位。
  • @TheSharpieOne。使用查询选择器,您无需知道哪些事件。使用路由器功能来引导您的流量。他将添加/删除内容。只要查询选择器与包含他需要的所有元素的容器相关联。在子函数中,他可以测试为任何属性、值等激活事件的对象。
【解决方案3】:

试试这个。使用 document.querySelector 并指定要跟踪的事件。单击按钮或在文本框中输入一些文本

	<html>
	<script>
	function onLoad() {
//	  var myDiv = document.getElementById("myDiv");
//	  myDiv.addEventListener("myEvent",function(){alert("Yes!");});
//	  document.dispatchEvent(new CustomEvent("myEvent",{detail:null}));

		var myDiv = document.querySelector("#myDiv");
	
		myDiv.addEventListener("click", myEventHandler, false);
		myDiv.addEventListener("change", myEventHandler, false);
	}

	function myEventHandler(e)
	{
		alert('Element was '+e.target.id+'\nEvent was '+e.type);
	}
	</script>
	<body onload="onLoad()">
	<div id="myDiv">

	<input type="button" id= "Button 1" value="Button 1"><br>
	<input type="text" id="Text 2">
	
	
	</div>
	</body>
    </html>

【讨论】:

    【解决方案4】:

    我发布了一些解决方案,而不是真正的答案。 这会将自定义事件传播到 DOM 中的所有元素。

    <html>
    <script>
    document.dispatchCustomEvent = function(event) {
      function visit(elem,event) {
        elem.dispatchEvent(event);
        var child = elem.firstChild;
        while(child) {
          if(child.nodeType==1) visit(child,event);
          child = child.nextSibling;
        }
      }
      visit(document.documentElement,event);
    }
    
    function onLoad() {
      var myDiv = document.getElementById("myDiv");
      myDiv.addEventListener("myEvent",function(){alert("Yes!");});
      document.dispatchCustomEvent(new CustomEvent("myEvent",{detail:null}));
    }
    </script>
    <body onload="onLoad()">
    <div id="myDiv"></div>
    </body>
    </html>
    

    【讨论】:

      【解决方案5】:

      走极端。事件传播到它们被分派的元素并备份......因此,为了在任何和基本上所有元素上触发事件(因为不知道哪些元素需要事件),您可以找到所有元素的“结束” DOM 的“分支”并向它们发送事件。

      jQuery 可以很容易地选择分支的结尾

      function onLoad() {
        var myDiv = document.getElementById("myDiv");
        myDiv.addEventListener("myEvent", function() {
          alert("Yes!");
        });
        $(':not(:has(*))').each(function(i, el){
          el.dispatchEvent(new CustomEvent("myEvent", {
            detail: null,
            bubbles: true
          }));
        });
      }
      

      https://jsfiddle.net/dkqagnph/1/

      使用这将确保每个 DOM 元素(附加到主体)获取事件(假设没有元素停止传播本身),因此无需知道哪些元素正在侦听或关心事件。这不会分派到每个单独的元素,因为那将是矫枉过正,但只是结束,让冒泡就可以了。

      注意:具有多个子元素的元素将多次获得该事件。您可能希望有一种方法来确保您的代码确实运行了多次,或者不关心它是否运行了多次。

      【讨论】:

      • 您的示例可能会在具有许多子元素的元素上多次触发该事件。无论如何,由于我不关心事件到达节点的顺序,我可以从顶部开始。
      • @Rediska 是的,我添加了一个注释。您可能可以将一些唯一的 id 与事件一起传递,以帮助捕捉来自不同孩子的“相同”事件
      猜你喜欢
      • 1970-01-01
      • 2012-02-20
      • 2010-11-13
      • 1970-01-01
      • 1970-01-01
      • 2018-07-26
      • 2014-02-12
      • 2017-07-05
      • 1970-01-01
      相关资源
      最近更新 更多