在 Web 组件中获取事件目标

Get event target inside a web component

锚元素 (<a>) 是在用户与网络组件交互时创建的。问题是,当单击锚点时,我无法从 Web 组件的“外部”获取返回的锚点元素。

我添加了一个事件监听器 document 来监听点击事件。当单击 DOM 中某个元素时,我希望 e.target 成为被单击的元素。 在 Web 组件内某处单击的情况下,将返回自定义元素 (<fancy-list></fancy-list>) - 而不是单击的元素。

当阴影模式 DOM 设置为 open 时,DOM 应该可以访问。

class Fancylist extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });

    const wrapper = document.createElement('div');
    wrapper.innerHTML = `<ul></ul><button>Add item</button>`;

    shadow.appendChild(wrapper);

    this.on_root_click = this.on_root_click.bind(this);
  }

  connectedCallback() {
    this.ul_elm = this.shadowRoot.querySelector('ul');
    this.shadowRoot.addEventListener('click', this.on_root_click, false);
  }

  on_root_click(e){
    switch(e.target.nodeName){
      case 'BUTTON':
        this.ul_elm.innerHTML += '<li><a href="p1">List item</a></li>';
        break;
      case 'A':
        e.preventDefault();
        console.log('You clicked a link!');
        break;
    }
  }
}

customElements.define('fancy-list', Fancylist);
<!DOCTYPE html>
<html>
  <head>
    <title>List</title>
    <meta charset="utf-8" />
    <script type="text/javascript">
      document.addEventListener('DOMContentLoaded', e => {
        document.body.addEventListener('click', e => {
          //console.log(e.composedPath());
          console.log(e.target); // why is this not returning an anchor element when an anchor is clickend inside the <fancy-list>?
        }, false);
      }, false);
    </script>
  </head>
  <body>
  <h1>List</h1>
  <fancy-list></fancy-list>
  </body>
</html>

ShadowDOM的目的正是屏蔽ShadowHTML的内容DOM 从容器的角度来看。

这也是为什么重新定位内部事件以暴露 Shadow DOM 宿主的原因。

但是,你仍然可以通过获取Event.path数组的第一项属性来获取真正的目标。

event.path[0]

注意:当然它只适用于 open Shadow DOM.

class Fancylist extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });

    const wrapper = document.createElement('div');
    wrapper.innerHTML = `<ul></ul><button>Add item</button>`;

    shadow.appendChild(wrapper);

    this.on_root_click = this.on_root_click.bind(this);
  }

  connectedCallback() {
    this.ul_elm = this.shadowRoot.querySelector('ul');
    this.shadowRoot.addEventListener('click', this.on_root_click, false);
  }

  on_root_click(e){
    switch(e.target.nodeName){
      case 'BUTTON':
        this.ul_elm.innerHTML += '<li><a href="p1">List item</a></li>';
        break;
      case 'A':
        e.preventDefault();
        break;
    }
  }
}

customElements.define('fancy-list', Fancylist);
<!DOCTYPE html>
<html>
  <head>
    <title>List</title>
    <meta charset="utf-8" />
    <script type="text/javascript">
      document.addEventListener('DOMContentLoaded', e => {
        document.body.addEventListener('click', e => {
          console.log(e.path[0]);
        }, false);
      }, false);
    </script>
  </head>
  <body>
  <h1>List</h1>
  <fancy-list></fancy-list>
  </body>
</html>

2021 年更新

如评论所述,您现在应该使用 event.composedPath()