为什么点击传播似乎会跳过下一个堆叠上下文的顶部元素?

huangapple go评论93阅读模式
英文:

Why does click propagation seem to jump over the top element of the next stacking context?

问题

这是我的示例代码:

.overlay {
  position: fixed;
  width: 200px;
  height: 150px;
  top: 0;
  left: 0;
  z-index: 1;
  background-color: rgba(255, 0, 0, 0.7);
  cursor: pointer;
}

img.overlay__first-child {
  position: fixed;
  top: -25px;
  right: -25px;
  width: 50px;
  height: 50px;
  z-index: 3;
  cursor: pointer;
  background-color: green;
}

.overlay img {
  position: relative;
  z-index: 2;
}

.overlay__second-child {
  width: 100px;
  height: 100px;
  background-color: blue;
}
<div class="no-styling" onclick="console.log('no-styling')">
  <div class="overlay" onclick="console.log('overlay')">
    <img src="#" class="overlay__first-child" onclick="console.log('overlay__first-child')" />
    <img src="#" class="overlay__second-child" onclick="console.log('overlay__second-child')" />
  </div>

  <p onclick="console.log('p')">Content to create a scrollbar</p>
  <p onclick="console.log('p')">Content to create a scrollbar</p>
  <p onclick="console.log('p')">Content to create a scrollbar</p>
  <p onclick="console.log('p')">Content to create a scrollbar</p>
  <p onclick="console.log('p')">Content to create a scrollbar</p>
  <p onclick="console.log('p')">Content to create a scrollbar</p>
  <p onclick="console.log('p')">Content to create a scrollbar</p>
  <p onclick="console.log('p')">Content to create a scrollbar</p>
  <p onclick="console.log('p')">Content to create a scrollbar</p>
  <p onclick="console.log('p')">Content to create a scrollbar</p>
  <p onclick="console.log('p')">Content to create a scrollbar</p>
  <p onclick="console.log('p')">Content to create a scrollbar</p>
</div>

如果我点击覆盖在p标签后面的文本,控制台不会打印p,但会打印no-styling。如果我将p标签更改为button标签,我会看到相同的传播跳转。另一方面,如果我直接点击p元素,它会像预期的那样记录pno-styling。我已在Firefox和Google Chrome上进行了测试,结果相同。

为了澄清,我在此截图中添加了黄色点以显示我点击的位置:

为什么点击传播似乎会跳过下一个堆叠上下文的顶部元素?

除了最低点之外的每个黄色点都会打印overlay__first-childoverlay__second-child(如果适用);overlay;和no-styling,但这些点击都不会打印p。最低点会打印no-stylingp

为什么点击覆盖会跳过p标签的onclick事件?

英文:

Here's my example code:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-css -->

.overlay {
  position: fixed;
  width: 200px;
  height: 150px;
  top: 0;
  left: 0;
  z-index: 1;
  background-color: rgba(255, 0, 0, 0.7);
  cursor: pointer;
}

img.overlay__first-child {
  position: fixed;
  top: -25px;
  right: -25px;
  width: 50px;
  height: 50px;
  z-index: 3;
  cursor: pointer;
  background-color: green;
}

.overlay img {
  position: relative;
  z-index: 2;
}

.overlay__second-child {
  width: 100px;
  height: 100px;
  background-color: blue;
}

<!-- language: lang-html -->

&lt;div class=&quot;no-styling&quot; onclick=&quot;console.log(&#39;no-styling&#39;)&quot;&gt;
  &lt;div class=&quot;overlay&quot; onclick=&quot;console.log(&#39;overlay&#39;)&quot;&gt;
    &lt;img src=&quot;#&quot; class=&quot;overlay__first-child&quot; onclick=&quot;console.log(&#39;overlay__first-child&#39;)&quot; /&gt;
    &lt;img src=&quot;#&quot; class=&quot;overlay__second-child&quot; onclick=&quot;console.log(&#39;overlay__second-child&#39;)&quot; /&gt;
  &lt;/div&gt;

  &lt;p onclick=&quot;console.log(&#39;p&#39;)&quot;&gt;Content to create a scrollbar&lt;/p&gt;
  &lt;p onclick=&quot;console.log(&#39;p&#39;)&quot;&gt;Content to create a scrollbar&lt;/p&gt;
  &lt;p onclick=&quot;console.log(&#39;p&#39;)&quot;&gt;Content to create a scrollbar&lt;/p&gt;
  &lt;p onclick=&quot;console.log(&#39;p&#39;)&quot;&gt;Content to create a scrollbar&lt;/p&gt;
  &lt;p onclick=&quot;console.log(&#39;p&#39;)&quot;&gt;Content to create a scrollbar&lt;/p&gt;
  &lt;p onclick=&quot;console.log(&#39;p&#39;)&quot;&gt;Content to create a scrollbar&lt;/p&gt;
  &lt;p onclick=&quot;console.log(&#39;p&#39;)&quot;&gt;Content to create a scrollbar&lt;/p&gt;
  &lt;p onclick=&quot;console.log(&#39;p&#39;)&quot;&gt;Content to create a scrollbar&lt;/p&gt;
  &lt;p onclick=&quot;console.log(&#39;p&#39;)&quot;&gt;Content to create a scrollbar&lt;/p&gt;
  &lt;p onclick=&quot;console.log(&#39;p&#39;)&quot;&gt;Content to create a scrollbar&lt;/p&gt;
  &lt;p onclick=&quot;console.log(&#39;p&#39;)&quot;&gt;Content to create a scrollbar&lt;/p&gt;
  &lt;p onclick=&quot;console.log(&#39;p&#39;)&quot;&gt;Content to create a scrollbar&lt;/p&gt;
&lt;/div&gt;

<!-- end snippet -->

If I click on the p text that's behind the overlay, the log doesn't print p, but it does print no-styling. If I change the p tags into button tags, I see the same propagation jump. On the other hand, if I click directly on a p element, it logs p and no-styling, as expected. I've tested this on Firefox and Google Chrome and see the same results.

For clarification, I've added yellow dots to this screenshot to show locations I'm clicking:

为什么点击传播似乎会跳过下一个堆叠上下文的顶部元素?

Every yellow dot except the lowest prints overlay__first-child and overlay__second-child, if applicable; overlay; and no-styling, but none of these clicks prints p. The lowest dot prints no-styling and p.

Why does clicking on the overlay skip the onclick of the p tags?

答案1

得分: 2

以下是翻译好的内容:

有两种不同的机制在这里起作用:

  • 确定哪个是接收点击事件的“前端”元素的机制

  • 从一个元素将接收到的元素冒泡到其在DOM层次结构中的祖先的机制。

第一种机制获取鼠标光标的坐标,并在具有该点区域的元素中查看这些元素的Z顺序(从前到后的顺序)。这种顺序不一定与DOM层次结构相关--它可以与DOM层次结构完全不同,因为它可以由CSS自由确定。在您的示例中,覆盖元素在p元素前面呈现。这意味着如果点击发生在包含在覆盖元素中的坐标上,无论是否还有一个p元素具有该坐标,最初的点击事件都是由覆盖元素接收的。或者,如果点击发生在覆盖区域之外,但仍在p元素的区域内,那么最初的点击事件将由该p元素接收。

第二种机制涉及事件冒泡。一些事件会冒泡,而其他事件不会。点击事件会冒泡。冒泡意味着在原始目标元素接收到点击事件后,根据DOM层次结构,无论这些元素在何处呈现,它的祖先也会接收到点击事件,直到DOM层次结构的根元素。请注意:点击事件的任何处理程序都可以取消此冒泡,以防它进一步传播到DOM层次结构的更高层。

我认为使用这些原则可以解释您示例的行为。

英文:

There are two different mechanisms going on here:

  • The mechanism that determines which is the "front" element that will receive the click event

  • The mechanism that bubbles a received element from an element to its ancestors in the DOM hierarchy.

The first mechanism takes the coordinates of the cursor (mouse) and, among the elements that have that point in their area, looks at the Z-order in which these elements are ordered (front-to-back ordering). This ordering is not necessarily related to the DOM hierarchy -- it can be completely different from it as it can be freely determined by CSS. In your example case the overlay element is rendered in front of the p elements. That means that if the click happens on a coordinate that is included in the overlay element, it is the overlay element that gets the initial click event, no matter whether there is also a p element that has that coordinate. Or, if the click happens outside the overlay area, but still on the area of a p element, it is that p element that gets the initial click event.

The second mechanism concerns event bubbling. Some events bubble, others don't. Click events bubble. Bubbling means that after the original target element received the click event, its ancestors -- according to the DOM hierarchy, irrespective where those elements are rendered -- also get a click event, up to the root element of the DOM hierarchy. NB: any of the handlers of the click event can cancel this bubbling, so it does not propagate further up the DOM hierarchy.

I think with these principles the behaviour of your example is explained.

答案2

得分: 1

好的,以下是已经翻译好的部分:

"Alright the solution to this is to use bubbles and I shall show you how exactly, with an example:"

"好的,解决这个问题的方法是使用事件冒泡,我将用一个示例来详细说明:"

"const containerElement = document.querySelector('.container');"
"const containerChildElement = document.querySelector('.containerChild');"

"containerElement.addEventListener('click', ({ target }) => console.log(target));"

"containerElement.addEventListener('click', ({ target }) => console.log(target));"

"Alright so how this works is it adds an event listener to the parent element (containerElement) which is supposedly behind the child element, there is a concept called bubbles which basically says the event must bubble up through the DOM tree and goes through all the children, the event.target property will store the childElement if you click on the part where the child and parent are overlapping."

"好的,这个方法的工作原理是向父元素(containerElement)添加了一个事件监听器,该父元素位于子元素后面。这里有一个概念叫做事件冒泡,它基本上表示事件必须在DOM树中冒泡,并经过所有子元素,如果您点击子元素和父元素重叠的部分,事件的 event.target 属性将存储子元素。"

"The code might be slightly hard to read if you aren't familiar with destructuring, basically the ({ target }) part just means event.target."

"如果您不熟悉解构的话,这段代码可能会稍微难以阅读,基本上,'({ target })' 部分只是表示 event.target。"

"Hope this helps."

"希望这有所帮助。"

英文:

Alright the solution to this is to use bubbles and I shall show you how exactly, with an example:

const containerElement = document.querySelector(&#39;.container&#39;);
const containerChildElement = document.querySelector(&#39;.containerChild&#39;);

containerElement.addEventListener(&#39;click&#39;, ({ target }) =&gt; console.log(target));

Alright so how this works is it adds an event listener to the parent element (containerElement) which is supposedly behind the child element, there is a concept called bubbles which basically says the event must bubble up through the DOM tree and goes through all the children, the event.target property will store the childElement if you click on the part where the child and parent are overlapping.

The code might be slightly hard to read if you aren't familar with destructuring, basically the ({ target }) part just means event.target.

Hope this helps.

huangapple
  • 本文由 发表于 2023年6月9日 03:07:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76434991.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定