首页 文章

确定用户是否在阴影dom外部单击

提问于
浏览
8

我正在尝试实现一个下拉菜单,你可以点击外面关闭 . 下拉列表是自定义日期输入的一部分,并封装在输入的shadow DOM中 .

我想写一些类似的东西:

window.addEventListener('mousedown', function (evt) {
  if (!componentNode.contains(evt.target)) {
    closeDropdown();
  }
});

但是,事件被重新定位,因此 evt.target 始终是元素的外部 . 事件在到达窗口之前会有多个阴影边界,因此似乎无法真正了解用户是否在组件内部单击 .

Note: 我没有在任何地方使用聚合物 - 我需要一个适用于通用阴影DOM的答案,而不是聚合物特定的黑客 .

3 回答

  • 1

    shadowRootevent.target 将是 host 元素 . 要关闭 shadowDOM 中的 <select> 元素,如果 event.target 不是 host 元素,则可以使用 if (evt.target !== hostElement) ,然后在 hostElement 上调用 .blur()

    var input = document.querySelector("input");
    var shadow = input.createShadowRoot();
    var template = document.querySelector("template");
    var clone = document.importNode(template.content, true);
    shadow.appendChild(clone);
    
    window.addEventListener("mousedown", function (evt) {
      if (evt.target !== input) {
        input.blur();
      }
    });
    
    <input type="date" />
    <template>
      <select>
        <option value="1999">1999</option>
        <option value="2000">2000</option>
      </select>
    </template>
    
  • 6

    另一个选项是检查目标元素的事件光标偏移:

    listener(event) {
        const { top, right, bottom, left } = targetElement.getBoundingClientRect();
        const { pageX, pageY } = event;
        const isInside = pageX >= left && pageX <= right && pageY >= top && pageY <= bottom;
    }
    
  • 0

    您可以尝试使用 event 对象的 path 属性 . 没有找到它的实际参考,MDN还没有它的页面 . HTML5Rocks虽然在影子dom教程中有一小部分关于它 . 因此,我不知道跨浏览器的兼容性 .

    找到W3 Spec关于事件路径,不确定这是否完全适用于 Event.path 属性,但它是我能找到的最接近的参考 .

    如果有人知道对Event.path的实际规范引用(如果链接的规范页面不是它),可以随意编辑它 .

    它保留了事件经历的路径 . 它将包含阴影dom中的元素 . 列表中的第一个元素( path[0] )应该是实际单击的元素 . 请注意,您需要从阴影dom参考中调用 contains ,例如 shadowRoot.contains(e.path[0]) 或阴影dom中的某个子元素 .

    演示:单击菜单展开,单击任意位置除菜单项将关闭菜单 .

    var host = document.querySelector('#host');
    var root = host.createShadowRoot();
    d = document.createElement("div");
    d.id = "shadowdiv";
    
    d.innerHTML = `
      <div id="menu">
        <div class="menu-item menu-toggle">Menu</div>
        <div class="menu-item">Item 1</div>
        <div class="menu-item">Item 2</div>
        <div class="menu-item">Item 3</div>
      </div>
      <div id="other">Other shadow element</div>
    `;
    var menuToggle = d.querySelector(".menu-toggle");
    var menu = d.querySelector("#menu");
    menuToggle.addEventListener("click",function(e){
      menu.classList.toggle("active");
    });
    root.appendChild(d)
    
    //Use document instead of window
    document.addEventListener("click",function(e){
      if(!menu.contains(e.path[0])){
        menu.classList.remove("active");
      }
    });
    
    #host::shadow #menu{
      height:24px;
      width:150px;
      transition:height 1s;
      overflow:hidden;
      background:black;
      color:white;
    }
    #host::shadow #menu.active {
      height:300px;
    }
    #host::shadow #menu .menu-item {
      height:24px;
      text-align:center;
      line-height:24px;
    }
    
    #host::shadow #other {
      position:absolute;
      right:100px;
      top:0px;
      background:yellow;
      width:100px;
      height:32px;
      font-size:12px;
      padding:4px;
    }
    
    <div id="host"></div>
    

相关问题