Пользовательский элемент getRootNode.closest (), пересекающий несколько (родительских) границ shadowDOM - PullRequest
0 голосов
/ 04 февраля 2019

Я потратил некоторое время на поиск, но видел только слишком много обычных блогов или ответов "ходи по DOM", которые идут только на один уровень ВВЕРХ с getRootnode()

псевдокодом:

HTML

<element-x>
//# shadow-root
    <element-y>
        <element-z>
        //# shadow-root
        let container = this.closest('element-x');
        </element-z>
    </element-y>
</element-x>

Стандартная функция element.closest() выполняет , а не прохождение теневых границ;

Итак, this.closest('element-x') возвращает null, поскольку в <element-z> <element-x> *1023* *1022*

1026 * Цель:

Найти <element-x> изнутри потомка <element z> (любой вложенный уровень)

Требуется:

A (рекурсивная) .closest() функция, которая поднимается вверх по (shadow) DOM s и находит <element-x>

Примечание: элементы могут иметь или не иметь ShadowDOM (см. <element y>: только lightDOM)

Я могу и буду делатьэто сам завтра;Я просто подумал, что какой-то яркий ум уже сделал это.

Ресурсы:

Обновление

Это Унифицированный код из ответа ниже:

        closestElement(selector, base = this) {
            function __closestFrom(el) {
                if (!el || el === document || el === window) return null;
                let found = el.closest(selector);
                return found ? found : __closestFrom(el.getRootNode().host);
            }

            return __closestFrom(base);
        }

Ответы [ 2 ]

0 голосов
/ 13 мая 2019

Отличные примеры!Хотел добавить версию TypeScript, которая имеет небольшое отличие - она ​​следует за параметром assignSlot при обходе теневых корней, чтобы вы могли найти ближайший соответствующий элемент в цепочке вложенных пользовательских элементов со слотами.Это не самый причудливый способ написания TypeScript, но он выполняет свою работу.

closestElement(selector: string, base: Element = this) {
  function __closestFrom(el: Element | Window | Document): Element {
    if (!el || el === document || el === window) return null;
    if ((el as Slotable).assignedSlot) el = (el as Slotable).assignedSlot;
    let found = (el as Element).closest(selector);
    return found
      ? found
      : __closestFrom(((el as Element).getRootNode() as ShadowRoot).host);
  }
  return __closestFrom(base);
}

Эквивалент в JS:

closestElement(selector, base = this) {
    function __closestFrom(el) {
        if (!el || el === document || el === window)
            return null;
        if (el.assignedSlot)
            el = el.assignedSlot;
        let found = el.closest(selector);
        return found
            ? found
            : __closestFrom(el.getRootNode().host);
    }
    return __closestFrom(base);
}
0 голосов
/ 05 февраля 2019

Это то же самое, что и .closest () изнутри любого дочернего (теневого) DOM

, но проходящего через DOM пересечение Границы теневого корня

Оптимизировано для (экстремального) минимизации

//declared as method on a Custom Element:
closestElement(
    selector,      // selector like in .closest()
    base = this,   // extra functionality to skip a parent
    __Closest = (el, found = el && el.closest(selector)) => 
        !el || el === document || el === window
            ? null // standard .closest() returns null for non-found selectors also
            : found 
                ? found // found a selector INside this element
                : __Closest(el.getRootNode().host) // recursion!! break out to parent DOM
) {
    return __Closest(base);
}

Примечание: функция __Closest объявляется как 'параметр', чтобы избежать дополнительного объявления let ... лучше дляминимизации и не дает вашей IDE жаловаться

Вызывается изнутри пользовательского элемента:

<element-x>
//# shadow-root
    <element-y>
        <element-z>
        //# shadow-root
        let container = this.closestElement('element-x');
        </element-z>
    </element-y>
</element-x>
...