在我的真实世界中,我处理的是一个反方向组件,一旦外部的任何元素都得到了关注,我就会关闭它。这个飞出组件被用作其他元素的阴影模板的一部分。当然,我的真实情况是,有更多的组件参与其中。我把这个案子减到了最低限度。
在下面的示例中,我们有一个外部和一个inner-component。内在的一个只不过是一个普通的插槽和一个专注的倾听者作为我的问题的演示者。outer-component包含一个未命名的插槽和一个内部组件,其中包含一个命名槽.
因此,内部组件将从外部组件和其outer-nested-slot中选择槽inner-plain-slot。最后,它将包含带有两个按钮的div。
现在,问题本身来了:按钮不是inner-component的真正子级。尽管如此,focusout-event还是会被接收,因为它们被排入inner-component。是否有一种方法可以编程地检查一个元素在逻辑上是否是另一个组件的子元素,即使在光域中没有父-子关系?
当然slot.assignedNodes({flatten:true})进了我的薄荷糖。但这只会返回我的按钮周围的包裹div。因此,我必须对所有返回的节点进行迭代,以检查其中任何节点是否包含相关元素。当然,如果分配的节点不是一个简单的div,而是一个was组件,那么一切都变得非常复杂。
因此,简单地说:给定一个webcomponent w-a和一个节点b,是否有一种方法以编程方式检查是否存在逻辑的父-子关系(这意味着一个事件可能从b到w-a )?
const stamp = (node, name) => {
const template = document.getElementById(name);
const templateContent = template.content;
const shadowRoot = node.attachShadow({
mode: 'open'
});
shadowRoot.appendChild(templateContent.cloneNode(true));
}
customElements.define('inner-element',
class extends HTMLElement {
constructor() {
super();
stamp(this, 'inner-element');
this.addEventListener('focusout', this.onFocusOut);
}
onFocusOut(focusevent) {
const newFocus = focusevent.relatedTarget;
const oldFocus = focusevent.target;
document.getElementById('log').innerHTML += `<div>${oldFocus?oldFocus.id:'null'} -> ${newFocus?newFocus.id:'null'}; oldFocus is Child? ${this.contains(oldFocus)}. newFocus is Child? ${this.contains(newFocus)}</div>`;
}
}
);
customElements.define('outer-element',
class extends HTMLElement {
constructor() {
super();
stamp(this, 'outer-element');
}
}
);<template id="inner-element">
<style>
:host {
border: 2px solid hotpink;
margin:2px;
padding: 2px;
}
</style>
<slot id="inner-plain-slot"></slot>
</template>
<template id="outer-element">
<style>
:host {
border: 2px solid rebeccapurple;
margin:2px;
padding: 2px;
display:flex;
flex-direction:row
}
</style>
<inner-element> <slot id="outer-nested-slot" name="nested"></slot> </inner-element>
<slot id="outer-plain-slot"></slot>
</template>
<outer-element>
<div style="display:flex;flex-direction:row" slot="nested">
<button id="nest1">nested-1</button>
<button id="nest2">nested-2</button>
</div>
<button id="not-nest1">not-nested1</button>
<button id="not-nest2">not-nested2</button>
</outer-element>
<div>
<code id=log></code>
</div>
我找到了一个可能的解决方案,使用事件本身。在浓缩了这个问题之后,解决办法似乎相当明显。但我还是更喜欢用一种本土化的方式来进行检查。
const checkChildParentRelation = (potentialChild, potentialParent) => {
if (!potentialChild || !potentialParent) {
return false;
}
let result = false;
const listener = e => {
result = true;
e.stopImmediatePropagation();
};
const eventName = `parent-child-test-event-${Date.now()}`;
potentialParent.addEventListener(eventName, listener);
potentialChild.dispatchEvent(
new CustomEvent(eventName, {
bubbles: true,
composed: true,
cancelable: true,
})
);
potentialParent.removeEventListener(eventName, listener);
return result;
};发布于 2022-09-10 18:31:19
影子DOM的一个大问题以及它与事件的关系是,当阴影DOM被捕获时,带有影子DOM的事件的事件目标将是宿主元素。这是“重定向” --这意味着如果在Shadow DOM中附加一个事件侦听器,目标将正常工作。如果将相同的事件侦听器附加到类似window的内容,那么目标将是影子DOM中任何元素的影子主机元素。
我无法找到一种简单的方法来完成您仅使用DOM所做的工作--当事件侦听器连接到阴影DOM之外的节点时,类似于Node#contains的东西总是返回false,并且您试图确定影子DOM的“目标”(将被重定向)是否包含阴影DOM中的另一个节点。所以我写了这个:
// Find the document-fragment or document node which contains the given node
export function findRootNode(n: Node): Node {
if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
if (n instanceof ShadowRoot) {
return n.host;
}
// I'm not sure when this could happen, but I don't expect it.
throw ("found document fragment that is not a shadow root");
}
if (n.nodeType === Node.DOCUMENT_NODE) {
return n;
}
return findRootNode(n.parentNode);
}
// findEventTarget will find what the event target (with respect to Window)
// should be for this element:
// - if in a shadow DOM then the shadow host element
// - otherwise, the element itself
export function findEventTarget(n: Node): Node {
const rootNode = findRootNode(n);
if (rootNode === document) {
// not in shadow dom
return n;
}
return rootNode;
}这些可以用来查找应该将事件的目标与哪个节点进行比较。如果在阴影DOM A中有一些节点S上的事件,那么您可以使用const et = findEventTarget(A)并将et与事件的目标进行比较。这将在不同阴影区域的组件之间工作。如果您想将其扩展到在影子DOM之间和阴影DOM内部工作,则可以使用Node.contains与“真实”节点的组合,如果返回null,则使用Node.contains和从findEventTarget()返回的Node。
发布于 2022-04-14 10:41:53
对于松散耦合事件最好。
在我看来,您想要处理执行event.composedPath()函数时获得的DOM元素数组。
https://stackoverflow.com/questions/71866583
复制相似问题