Shadow DOM and Events

Shadow DOM and Events (Shadow DOM和事件)

The main aim of the shadow tree is encapsulating the component’s internal implementation details. (阴影树的主要目的是封装组件的内部实现细节。)

The events occurring inside the shadow DOM take the host element as the target once caught out of the component. (在shadow DOM内发生的事件将宿主元素作为目标,一旦从组件中捕获出来。)

Here is an example:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <site-card></site-card>
   <script>
     customElements.define('site-card', class extends HTMLElement {
       connectedCallback() {
         this.attachShadow({
           mode: 'open'
         });
         this.shadowRoot.innerHTML = `<p>
           <button>Click on button</button>
         </p>`;
         this.shadowRoot.firstElementChild.onclick =
           e => alert("Inner target: " + e.target.tagName);
       }
     });
     document.onclick = e => alert("Outer target: " + e.target.tagName);
   </script>
 </body>
</html>

Once you click the button, the following messages will be shown:

Inner target: BUTTON - the internal event handler receives the correct target, the element within the shadow DOM. Outer target:USER-CARD - the document event handler receives the shadow host as a target.

Event targeting is а very practical technique to have, as the outer document shouldn’t know about component internals. From the outer document point of view, that takes place on <user-card> .

If the event triggers on a slotted element physically living in the light DOM, retargeting won’t occur. (如果事件在物理上位于光DOM中的插槽元素上触发,则不会发生重定向。)

Let’s check out an example:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <site-card id="siteCard">
     <span slot="sitename">Welcome to w3cdoc</span>
   </site-card>
   <script>
     customElements.define('site-card', class extends HTMLElement {
       connectedCallback() {
         this.attachShadow({
           mode: 'open'
         });
         this.shadowRoot.innerHTML = `<div>
           <b>Name:</b> <slot name="sitename"></slot>
         </div>`;
         this.shadowRoot.firstElementChild.onclick =
           e => alert("Inner target: " + e.target.tagName);
       }
     });
     siteCard.onclick = e => alert(`Outer target: ${e.target.tagName}`);
   </script>
 </body>
</html>

Bubbling, Event.composedPath()

Bubbling, Event.composedPath() (Bubbling, Event.composedPath ())

For the event bubbling purposes, it is recommended to use the flattened DOM. (出于事件冒泡的目的,建议使用扁平化的DOM。)

So, when there is a slotted element, and an event triggers somewhere within it, then it will bubble up to the <slot> and upwards.

The entire path to the original event target may be obtained with event.composedPath(). That process is taken from the composition. (可以使用event.composedPath ()获取原始事件目标的整个路径。该过程取自组合物。)

In the example demonstrated above, the flattened DOM looks like this:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <site-card id="siteCard">
     #shadow-root
     <div>
       <b>Name:</b>
       <slot name="sitename">
         <span slot="sitename">w3cdoc</span>
       </slot>
     </div>
   </site-card>
 </body>
</html>

So, clicking on <span slot=“username”> and calling event.composedPath() event.composedPath() returns an array [span, slot, div, shadow-root, user-card, body, html, document, window]. It is the parent chain. Please, also take into consideration that once the shadow tree is created using {mode: ‘closed’}, then the composed path begins from the host: user-card and upwards.

Event Composed

Most of the events effectively bubble through shadow DOM boundary. Only a few events can’t do that. (大多数事件都有效地通过阴影DOM边界。只有少数活动无法做到这一点。)

It is governed by the composed event object property. When it’s true, then the event crosses the boundary. In the opposite case, it is only caught from inside the shadow DOM. (它由组合的事件对象属性控制。如果为真,则事件跨越边界。在相反的情况下,它只能从阴影DOM内部捕获。)

In the UI Events Specification, the majority of the events include composed: true. They are as follows:

  • blur, focus, focusin, focusout, (-模糊、聚焦、聚焦、聚焦输出、)

  • click, dblclick, (- click、dblclick、)

  • mousedown, mouseup mousemove, mouseout, mouseover, (- mousedown、mouseup、mouseemove、mouseout、mouseover、)

  • wheel, (车裂)

  • beforeinput, input, keydown, keyup. (-输入前,输入,键入,键入。)

All the touch and pointer events also include composed: true.

However, several events include composed: false. They are as follows:

  • mouseenter, mouseleave (they never bubble), (- mouseenter、mouseleave (它们从不冒泡)、)

  • load, unload, abort, error, (-加载、卸载、中止、错误,)

  • select, (选择)

  • slotchange (-槽位更换)

You can only catch these events on elements inside the same DOM where the event target exists. (只能在事件目标所在的同一DOM内的元素上捕获这些事件。)

Custom Events

Custom Events (自定义事件)

Once dispatching custom events, it is necessary to set both bubbles and composed properties to true. It should be done for bubbling up and out of the component. (调度自定义事件后,有必要将气泡和组合属性都设置为true。应该这样做是为了冒泡和脱离组件。)

The example is demonstrated below:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <div id="outer"></div>
   <script>
     outer.attachShadow({
       mode: 'open'
     });
     let inner = document.createElement('div');
     outer.shadowRoot.append(inner);
     /*
     div(id=outer)
(div (id = outer))
       #shadow-dom
         div(id=inner)
(div (id = inner))
     */
     document.addEventListener('test', event => alert(event.detail));
     inner.dispatchEvent(new CustomEvent('test', {
       composed: true,
       bubbles: true,
       detail: "composed"
     }));
     inner.dispatchEvent(new CustomEvent('test', {
       composed: false,
       bubbles: true,
       detail: "not composed"
     }));
   </script>
 </body>
</html>

Summary

Summary (概要)

Events are capable of crossing shadow DOM boundaries in case their composed is set to true. (事件能够跨越阴影DOM边界,以防其组合设置为true。)

Most of the built-in events encompass composed: true. Yet, some of them have composed: false. They can be caught merely on elements inside the same DOM. Dispatching a CustomEvent will explicitly set composed: true.



请遵守《互联网环境法规》文明发言,欢迎讨论问题
扫码反馈

扫一扫,反馈当前页面

咨询反馈
扫码关注
返回顶部