Fix bug when component appears nested in shadow-DOM(s)
This commit is contained in:
parent
3f9ff76d93
commit
a39e6bcfb5
1 changed files with 40 additions and 6 deletions
|
@ -13,6 +13,21 @@ const template = fn => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Outwards iterator over an element's root nodes across shadow DOM boundaries
|
||||||
|
* @param {Element} element
|
||||||
|
*/
|
||||||
|
const ancestorRoots = function*(element) {
|
||||||
|
while (true) {
|
||||||
|
const root = element.getRootNode()
|
||||||
|
yield {root,element}
|
||||||
|
if (root instanceof ShadowRoot) {
|
||||||
|
element = root.host
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const f = template(string => {
|
const f = template(string => {
|
||||||
const template = document.createElement("template")
|
const template = document.createElement("template")
|
||||||
template.innerHTML = string
|
template.innerHTML = string
|
||||||
|
@ -27,6 +42,7 @@ const css = template(string => {
|
||||||
|
|
||||||
const childObserver = new MutationObserver(mutations => {
|
const childObserver = new MutationObserver(mutations => {
|
||||||
for (const mutation of mutations) {
|
for (const mutation of mutations) {
|
||||||
|
if (mutation.target instanceof BetterSelect)
|
||||||
mutation.target.mutationCallback()
|
mutation.target.mutationCallback()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -195,11 +211,29 @@ export class BetterSelect extends HTMLElement {
|
||||||
this.#abortOpen = new AbortController()
|
this.#abortOpen = new AbortController()
|
||||||
|
|
||||||
const signal = this.closeSignal
|
const signal = this.closeSignal
|
||||||
window.addEventListener("click", event => {
|
|
||||||
if (event.target instanceof HTMLElement && !this.contains(event.target)) {
|
// Click events don't properly cross shadow-DOM boundaries.
|
||||||
|
// Therefore: one event is needed for each nested shadow-DOM
|
||||||
|
// in the element's ancestry.
|
||||||
|
for (const {root, element} of ancestorRoots(this)) {
|
||||||
|
root.addEventListener("click", event => {
|
||||||
|
if (event.target instanceof HTMLElement) {
|
||||||
|
// This can only happen within the same root as the
|
||||||
|
// current element so can be handled trivially
|
||||||
|
if (this.contains(event.target)) return
|
||||||
|
|
||||||
|
// On every level, if an event originates from the containing
|
||||||
|
// shadow host, it can get ignored, as the corresponding
|
||||||
|
// shadow root event handler has already handled it.
|
||||||
|
if (event.target == element) return
|
||||||
|
|
||||||
|
// The event target wasn't inside the element
|
||||||
|
// nor is indirectly hosting it.
|
||||||
this.close()
|
this.close()
|
||||||
}
|
}
|
||||||
}, {signal})
|
}, {signal})
|
||||||
|
}
|
||||||
|
|
||||||
this.addEventListener("keypress", event => {
|
this.addEventListener("keypress", event => {
|
||||||
if (event.key == "Enter") {
|
if (event.key == "Enter") {
|
||||||
this.selectDefault()
|
this.selectDefault()
|
||||||
|
|
Loading…
Reference in a new issue