【问题标题】:Get event target inside a web component获取 Web 组件内的事件目标
【发布时间】:2020-01-17 16:08:19
【问题描述】:

锚元素 (<a>) 在用户与 Web 组件交互时创建。问题是,当单击锚点时,我无法从 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>

【问题讨论】:

    标签: javascript dom events web-component shadow-dom


    【解决方案1】:

    Shadow DOM 的目的正是掩盖Shadow DOM 从容器的角度来看的HTML 内容。

    这也是重新定位内部事件以公开 Shadow DOM 宿主的原因。

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

    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()

    【讨论】:

    • 感谢您的回答。它在 Firefox 中不起作用,它让我寻找更多答案——我发现了这个:stackoverflow.com/a/39245638/322084 显然我们对该功能有不同的实现,Event.path 和 Event.composedPath()。
    • 使用event.composedPath() 而不是event.path
    • 很遗憾,Web 组件的影子根中缺少事件:target。到目前为止,没有迹象表明任何浏览器都会解决这个问题。我想知道他们是否在等待 W3C 做某事或其他一些标准实体。
    猜你喜欢
    • 2019-02-20
    • 1970-01-01
    • 1970-01-01
    • 2020-05-07
    • 2019-10-26
    • 2010-12-13
    • 1970-01-01
    相关资源
    最近更新 更多