【问题标题】:Trigger an event on a shadow DOM element that is overlaid by an element in the light DOM (not a parent)在被 light DOM(不是父级)中的元素覆盖的 shadow DOM 元素上触发事件
【发布时间】:2019-09-01 21:16:17
【问题描述】:

我正在尝试获取阴影 DOM 表格单元格的坐标,该单元格可能(或可能不)在其前面显示覆盖元素。可点击的覆盖元素可能跨越多个单元格,因此它们不一定是我需要坐标的单元格的子级。

我无法让活动像我预期的那样在桌子上冒泡。我相信影子 DOM 的事件重新定位可以防止事件在事件源自轻型 DOM 时击中单元格(我有点认为无论如何这是错误的方法,因为覆盖元素不是我希望坐标开始的单元格)

“pointer-events: none;”属性几乎可以满足我的要求,但是,我需要该事件也可以在 light DOM 覆盖元素上触发,所以我不知道这是否是一个选项。

我认为最好避免将侦听器附加到每个单元以解决性能问题(在最终应用程序中将有数百个单元)

我正在使用原生 JavaScript

在提供的示例中,是否可以在单击覆盖的 div 灰色时获取底层表格单元格的正确坐标?

class Custom_Grid extends HTMLElement {
  constructor() {
    super();

    this.attachShadow({
      mode: 'open'
    });
    this.shadowRoot.innerHTML = `
        <style>
          td {
            height:50px; width:50px; border: 1px solid grey;            
          }
        </style>
        <table>
        <tr>
        <td id='x0y0'><slot name='x0y0'></slot></td>
        <td id='x0y1'><slot name='x0y1'></slot></td>
        <td id='x0y2'><slot name='x0y2'></slot></td>
        </tr>
        <tr>
        <td id='x1y0'><slot name='x1y0'></slot></td>
        <td id='x1y1'><slot name='x1y1'></slot></td>
        <td id='x1y2'><slot name='x1y2'></slot></td>
        </tr>
        <tr>
        <td id='x2y0'><slot name='x2y0'></slot></td>
        <td id='x2y1'><slot name='x2y1'></slot></td>
        <td id='x2y2'><slot name='x2y2'></slot></td>
        </tr>
        
      `;
  }
  connectedCallback() {
    this.shadowRoot.addEventListener("click", (e) => {
      if (e.target.matches('td'))
        console.log(`Grid Cell: ${e.target.id}`)
    });
  }

}

window.customElements.define('custom-grid', Custom_Grid);


document.getElementById('grid_overlay').addEventListener("click", (e) => {
  console.log("#overGrid Click")
});
#grid_overlay {
  width: 100px;
  height: 100px;
  background-color: grey;
  position: absolute;
  /*pointer-events: none; <-- not an option?*/
}
<custom-grid>
  <div slot='x1y0' id='grid_overlay'></div>
</custom-grid>

【问题讨论】:

    标签: javascript css addeventlistener shadow-dom


    【解决方案1】:

    一种解决方案是暂时隐藏覆盖元素,以便通过elementFromPoint() 方法获得被覆盖的元素。

    if ( e.target.id === 'grid_overlay' ) {        
       console.log( 'overlay click' )
       e.target.hidden = true 
       var el = this.shadowRoot.elementFromPoint( e.x, e.y )
       if (el.id)
         console.log( 'under: ', el.id )
       e.target.hidden = false
    }
    

    class Custom_Grid extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = `
            <style>
              td {
                height:50px; width:50px; border: 1px solid grey;            
              }
            </style>
            <table>
            <tr>
            <td id='x0y0'><slot name='x0y0'></slot></td>
            <td id='x0y1'><slot name='x0y1'></slot></td>
            <td id='x0y2'><slot name='x0y2'></slot></td>
            </tr>
            <tr>
            <td id='x1y0'><slot name='x1y0'></slot></td>
            <td id='x1y1'><slot name='x1y1'></slot></td>
            <td id='x1y2'><slot name='x1y2'></slot></td>
            </tr>
            <tr>
            <td id='x2y0'><slot name='x2y0'></slot></td>
            <td id='x2y1'><slot name='x2y1'></slot></td>
            <td id='x2y2'><slot name='x2y2'></slot></td>
            </tr>
            
          `;
      }
      connectedCallback() {
        this.shadowRoot.addEventListener("click", (e) => {
          if (e.target.matches('td'))
            console.log(`Grid Cell: ${e.target.id}`)
          else if (e.target.id === 'grid_overlay' ) {        
            console.log( 'overlay click ' )
            e.target.hidden = true 
            var el = this.shadowRoot.elementFromPoint( e.x, e.y )
            if (el.id)
              console.log( 'under: ', el.id )
            e.target.hidden = false
          }
        });
      }
    }
    
    window.customElements.define('custom-grid', Custom_Grid);
    #grid_overlay {
      width: 100px;
      height: 100px;
      background-color: grey;
      position: absolute;
    }
    <custom-grid>
      <div slot='x1y0' id='grid_overlay'></div>
    </custom-grid>

    【讨论】:

    • 这正是我想要的。如果偶然有人尝试使用作为另一个自定义元素的叠加层来执行此操作,您需要添加一个尊重隐藏属性的 :host 显示样式(否则隐藏属性被覆盖)。
    猜你喜欢
    • 1970-01-01
    • 2021-11-05
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 2020-11-22
    • 2020-03-16
    • 1970-01-01
    相关资源
    最近更新 更多