简体   繁体   English

为什么相同的 [click] 事件会被处理两次?

[英]Why would the same [click] event be handled twice?

The below code is a simplification of my problem.下面的代码是我的问题的简化。 When you click on a_element, b_element will show and an event listener will be created to listen for a second click, which will hide b_element.当您单击 a_element 时,将显示 b_element 并创建一个事件侦听器来侦听第二次单击,这将隐藏 b_element。 Problem is, the first click that makes b_element show is also triggering the nested event listener ie window.addEventListener .问题是,使 b_element 显示的第一次单击也触发了嵌套事件侦听器,即window.addEventListener Is there a way I can write this window event listener so it won't execute on the initial click?有没有办法我可以编写这个 window 事件监听器,这样它就不会在初始点击时执行?

 var a_element = document.getElementById("a"); var b_element = document.getElementById("b"); function show_block () { b_element.style.display = "block"; console.log("showing block"); a_element.removeEventListener('mouseup', show, false); window.addEventListener('mouseup', hide = function () { b_element.style.display = "none"; console.log("hiding block"); window.removeEventListener('mouseup', hide, false); a_element.addEventListener('mouseup', show = function () { show_block(); }, false); }, false); } a_element.addEventListener('mouseup', show = function () { show_block(); }, false);
 #a { position: absolute; height: 50px; width: 50px; background-color: rgba(100,120,140,1); } #b { display: none; position: absolute; left: 200px; height: 50px; width: 50px; background-color: rgba(200,220,240,1); }
 <div id="a"></div> <div id="b"></div>

And a simplified version:还有一个简化版:

 var a_block = document.getElementById("a"); function message () { window.addEventListener('mouseup', message = function () { console.log("you clicked twice"); }, false); } a_block.addEventListener('mouseup', clicked = function () { message(); console.log("you clicked once"); }, false);
 #a { height: 50px; width: 50px; position: absolute; background-color: #000000; }
 <div id="a"></div>

The event moves from the window to the target (capturing) and then back from the target to the window (bubbling).事件从 window 移动到目标(捕获),然后从目标返回到 window(冒泡)。 This happens for every event.这发生在每个事件中。 Since a_block is the target, it doesn't matter if I set it to true or false (true will trigger it at the very end of the capturing phase and false will trigger it at the very beginning of the bubbling phase but since a_block is the apex of the movement path it is essentially the same point).由于 a_block 是目标,因此我将其设置为 true 或 false 都没有关系(true 将在捕获阶段的最后触发它,而 false 将在冒泡阶段的最开始触发它,但由于 a_block 是运动路径的顶点基本上是同一点)。 So the event travels from the window, then to the target, which at that point creates the event listener at the window.因此,事件从 window 传播到目标,此时目标在 window 处创建事件侦听器。 Then the event, which is still at the target, travels from the target to the window.然后,仍位于目标的事件从目标传播到 window。 If the window event listener is set to bubbling phase ie false, then it will trigger when the event reaches the window.如果 window 事件监听器设置为冒泡阶段,即 false,那么它将在事件到达 window 时触发。 Otherwise, if the window event listener is set to the capturing phase ie true, the event will trigger on a capturing phase which can only happen at that point by clicking the mouse a second time.否则,如果 window 事件侦听器设置为捕获阶段,即 true,则事件将在捕获阶段触发,该捕获阶段只能通过第二次单击鼠标在该点发生。

捕获和冒泡阶段

TLDR: Change the nested listener to true: TLDR:将嵌套侦听器更改为 true:

 var a_block = document.getElementById("a"); function message () { window.addEventListener('mouseup', message = function () { console.log("you clicked twice"); }, true); } a_block.addEventListener('mouseup', clicked = function () { message(); console.log("you clicked once"); }, false);
 #a { height: 50px; width: 50px; position: absolute; background-color: #000000; }
 <div id="a"></div>

The reason the function referred to with hide is called with the first "click" is because mouse events first trigger all those event listeners attached to any ancestor (parent, grandparent, etc) of the element that is the "target" of the click (the actual element being interacted with), that are registered with the capture flag set (the second parameter of the addEventListener that you're using, if omitted the capture flag is set by default).hide引用的 function 被第一次“点击”调用的原因是,鼠标事件首先触发所有那些附加到作为点击“目标”的元素的任何祖先(父、祖父母等)的事件侦听器(正在与之交互的实际元素),使用捕获标志集注册(您正在使用的addEventListener的第二个参数,如果省略,则默认设置捕获标志)。 This is called the capture phase of event dispatching -- if you click the "a" element, if there was an event listener attached to the window (the ancestor for all elements with event dispatching) with the second argument to addEventListener set to true (or not specified at all), the event listener would be called first.这称为事件调度的捕获阶段——如果单击“a”元素,如果有一个事件侦听器附加到 window(所有具有事件调度的元素的祖先), addEventListener的第二个参数设置为true (或根本没有指定),事件监听器将首先被调用。 And so on towards the element, and ending with calling event listeners added on the element itself.以此类推,直到元素本身调用添加的事件侦听器。

After the "capture phase" above, the event "bubbles back up" upwards through the hierarchy of elements starting with the target element and ending with the window object.在上面的“捕获阶段”之后,事件在元素层次结构中向上“冒泡”,从目标元素开始,到 window object 结束。 This is called the "bubbling phase" -- it's just like with the "capture phase" for the event, but in reverse order, and this time only event listeners added with the second argument to addEventListner being set to false explicitly, are called, ending with event listener(s) added to the window object.这称为“冒泡阶段”——就像事件的“捕获阶段”一样,但顺序相反,这次只有添加了addEventListner的第二个参数被显式设置为false的事件侦听器被调用,以添加到 window object 的事件侦听器结束。

What happens in your case is that since you add an event listener to the window object with false as second argument to addEventListener while the event is still being dispatched, as the event finishes "bubbling up" and when it's turn to call event listeners (those added for the "bubbling phase") on the window object, your newly added (to the window) event listener is called!在您的情况下发生的情况是,由于您向 window object 添加了一个事件侦听器,并且在事件仍在调度时将false作为addEventListener的第二个参数,因为事件完成“冒泡”并且当轮到调用事件侦听器时(那些为 window object 上的“冒泡阶段”添加,您新添加的(到窗口)事件监听器被调用!

Like I said, I don't know why there is false specified for every call to addEventListener in your code, it's what I'd call "code smell" -- a sign that something is probably wrong.就像我说的,我不知道为什么在你的代码中每次调用addEventListener时都指定了false ,这就是我所说的“代码异味”——这表明可能有问题。 Which it happens to be the case, for better or worse?碰巧是这种情况,是好是坏? Do you have a reason to handle events during "bubbling" of these, as opposed to the default (no second argument to addEventListener specified) which is for during "capturing"?您是否有理由在这些“冒泡”期间处理事件,而不是在“捕获”期间使用默认值(没有指定addEventListener的第二个参数)? Incidentally, this is what has gotten you the trouble you're dealing with.顺便说一句,这就是给您带来麻烦的原因。 If in doubt, don't specify things -- you have at most equal chance of getting it wrong anyway, why take the risk with extra arguments that are confusing the reader, and incidentally bring about wrong behaviour?如果有疑问,请不要具体说明——无论如何,您最多有相同的机会出错,为什么要冒险使用额外的 arguments 让读者感到困惑,并顺便带来错误的行为?

Practically, there are other issues with your code -- you attach anonymous functions that do the same thing but are not the same function (as in, same object created once), you re-assign show and hide although these refer to the same de-facto behaviour -- meaning they should basically be the functions "show" and "hide", without elaborate anonymous functions being assigned to window properties (no declarations of show and hide , which makes these being assigned on the window/global object).实际上,您的代码还有其他问题 - 您附加了执行相同操作但不相同 function 的匿名函数(如创建一次相同的 object),您重新分配showhide ,尽管这些引用相同的 de -facto 行为——意味着它们基本上应该是函数“show”和“hide”,没有将详细的匿名函数分配给 window 属性(没有声明showhide ,这使得它们被分配在窗口/全局对象上)。

But without knowing what behaviour you want exactly, I am afraid I cannot provide you with any code.但是在不知道你到底想要什么行为的情况下,恐怕我无法为你提供任何代码。 Without knowing what you want, I cannot tell you what is wrong with the code, although all of the above information still applies, obviously -- I am just not sure how it applies in your case.在不知道您想要什么的情况下,我无法告诉您代码有什么问题,尽管显然以上所有信息仍然适用——我只是不确定它如何适用于您的情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM