确定用户是否单击了外部阴影 dom
Determine if user clicked outside shadow dom
我正在尝试实现一个下拉菜单,您可以在外部单击以将其关闭。下拉列表是自定义日期输入的一部分,封装在输入的阴影内 DOM.
我想写这样的东西:
window.addEventListener('mousedown', function (evt) {
if (!componentNode.contains(evt.target)) {
closeDropdown();
}
});
然而,事件是重定向的,所以 evt.target
总是 元素的外部。事件在到达 window 之前会跨越多个阴影边界,因此似乎无法真正知道用户是否在我的组件内单击。
注意:我没有在任何地方使用聚合物——我需要一个适用于通用阴影的答案DOM,而不是聚合物特定的 hack。
您可以尝试使用 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>
shadowRoot
的 event.target
将是 host
元素。如果 event.target
不是 host
元素,要关闭 shadowDOM
中的 <select>
元素,您可以使用 if (evt.target !== hostElement)
,然后在 [=21= 上调用 .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>
另一种选择是检查事件光标相对于目标元素的偏移量:
listener(event) {
const { top, right, bottom, left } = targetElement.getBoundingClientRect();
const { pageX, pageY } = event;
const isInside = pageX >= left && pageX <= right && pageY >= top && pageY <= bottom;
}
由于声誉原因无法发表评论,但想使用 composedPath
分享它的外观。参见
document.addEventListener("click",function(e){
if(!e.composedPath().includes(menu)){
menu.classList.remove("active");
}
});
我正在尝试实现一个下拉菜单,您可以在外部单击以将其关闭。下拉列表是自定义日期输入的一部分,封装在输入的阴影内 DOM.
我想写这样的东西:
window.addEventListener('mousedown', function (evt) {
if (!componentNode.contains(evt.target)) {
closeDropdown();
}
});
然而,事件是重定向的,所以 evt.target
总是 元素的外部。事件在到达 window 之前会跨越多个阴影边界,因此似乎无法真正知道用户是否在我的组件内单击。
注意:我没有在任何地方使用聚合物——我需要一个适用于通用阴影的答案DOM,而不是聚合物特定的 hack。
您可以尝试使用 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>
shadowRoot
的 event.target
将是 host
元素。如果 event.target
不是 host
元素,要关闭 shadowDOM
中的 <select>
元素,您可以使用 if (evt.target !== hostElement)
,然后在 [=21= 上调用 .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>
另一种选择是检查事件光标相对于目标元素的偏移量:
listener(event) {
const { top, right, bottom, left } = targetElement.getBoundingClientRect();
const { pageX, pageY } = event;
const isInside = pageX >= left && pageX <= right && pageY >= top && pageY <= bottom;
}
由于声誉原因无法发表评论,但想使用 composedPath
分享它的外观。参见
document.addEventListener("click",function(e){
if(!e.composedPath().includes(menu)){
menu.classList.remove("active");
}
});