Подтвердить что ты не робот

Как я могу определить, находится ли элемент в тени DOM?

У меня есть проект, где я использую теневое DOM изначально (не через polyfill). Я хотел бы определить, содержится ли данный element в теневой DOM или светлом DOM.

Я просмотрел все свойства элементов, но не кажется, что они не зависят от типа элемента DOM.

Как определить, является ли элемент частью теневого DOM или светлого DOM?


  Вот пример того, что считается "shadow DOM" и "light DOM" для этого вопроса.

 (light root) • Document
      (light)   • HTML
      (light)   | • BODY
      (light)   |   • DIV
(shadow root)   |     • ShadowRoot
     (shadow)   |       • DIV 
     (shadow)   |         • IFRAME 
 (light root)   |           • Document
      (light)   |             • HTML
      (light)   |             | • BODY
      (light)   |             |   • DIV
(shadow root)   |             |     • ShadowRoot
     (shadow)   |             |       • DIV
       (none)   |             • [Unattached DIV of second Document]
       (none)   • [Unattached DIV of first Document]

<!doctype html>
<title>
  isInShadow() test document - can not run in Stack Exchange sandbox
</title>
<iframe src="about:blank"></iframe>
<script>

function isInShadow(element) {
  // TODO
}

function test() {
  //  (light root) • Document
  //       (light)   • HTML
  var html = document.documentElement;

  console.assert(isInShadow(html) === false);

  //       (light)   | • BODY
  var body = document.body;

  console.assert(isInShadow(body) === false);

  //       (light)   |   • DIV
  var div = document.createElement('div');
  body.appendChild(div);

  console.assert(isInShadow(div) === false);

  // (shadow root)   |     • ShadowRoot
  var divShadow = div.createShadowRoot();

  var shadowDiv = document.createElement('div');
  divShadow.appendChild(shadowDiv);

  //      (shadow)   |       • DIV 
  console.assert(isInShadow(shadowDiv) === true);

  //      (shadow)   |         • IFRAME 
  var iframe = document.querySelector('iframe');
  shadowDiv.appendChild(iframe);

  console.assert(isInShadow(iframe) === true);

  //  (light root)   |           • Document
  var iframeDocument = iframe.contentWindow.document;

  //       (light)   |             • HTML
  var iframeHtml = iframeDocument.documentElement;

  console.assert(isInShadow(iframeHtml) === false);

  //       (light)   |             | • BODY
  var iframeBody = iframeDocument.body;

  //
  console.assert(isInShadow(iframeHtml) === false);

  //       (light)   |             |   • DIV
  var iframeDiv = iframeDocument.createElement('div');
  iframeBody.appendChild(iframeDiv);
   
  console.assert(isInShadow(iframeDiv) === false);
   
  // (shadow root)   |             |     • ShadowRoot
  var iframeDivShadow = iframeDiv.createShadowRoot();

  //      (shadow)   |             |       • DIV
  var iframeDivShadowDiv = iframeDocument.createElement('div');
  iframeDivShadow.appendChild(iframeDivShadowDiv);
    
  console.assert(isInShadow(iframeDivShadowDiv) === true);
     
  //        (none)   |             • [Unattached DIV of second Document]
  var iframeUnattached = iframeDocument.createElement('div');
    
  console.assert(Boolean(isInShadow(iframeUnattached)) === false);

  //        (none)   • [Unattached DIV of first Document]
  var rootUnattached = document.createElement('div');
    
  console.assert(Boolean(isInShadow(rootUnattached)) === false);
}

onload = function main() {
  console.group('Testing');
  try {
    test();
    console.log('Testing complete.');
  } finally {
    console.groupEnd();
  }
}

</script>
4b9b3361

Ответ 1

Если вы вызовете метод ShadowRoot toString(), он вернет "[object ShadowRoot]". Согласно этому факту, здесь мой подход:

function isInShadow(node) {
    var parent = (node && node.parentNode);
    while(parent) {
        if(parent.toString() === "[object ShadowRoot]") {
            return true;
        }
        parent = parent.parentNode;
    }
    return false;
}

ИЗМЕНИТЬ

Джереми Бэнкс предлагает подход в другом стиле цикла. Этот подход немного отличается от моего: он также проверяет пройденный node сам, чего я не делал.

function isInShadow(node) {
    for (; node; node = node.parentNode) {
        if (node.toString() === "[object ShadowRoot]") {
            return true;
        }
    }
    return false;
}

function isInShadow(node) {
    for (; node; node = node.parentNode) {
        if (node.toString() === "[object ShadowRoot]") {
            return true;
        }
    }
    return false;
}

console.group('Testing');

var lightElement = document.querySelector('div');    

console.assert(isInShadow(lightElement) === false);

var shadowChild = document.createElement('div');
lightElement.createShadowRoot().appendChild(shadowChild);

console.assert(isInShadow(shadowChild) === true);

var orphanedElement = document.createElement('div');

console.assert(isInShadow(orphanedElement) === false);

var orphanedShadowChild = document.createElement('div');
orphanedElement.createShadowRoot().appendChild(orphanedShadowChild);

console.assert(isInShadow(orphanedShadowChild) === true);

var fragmentChild = document.createElement('div');
document.createDocumentFragment().appendChild(fragmentChild);

console.assert(isInShadow(fragmentChild) === false);

console.log('Complete.');
console.groupEnd();
<div></div>

Ответ 2

⚠️ Предупреждение: риск устаревания

Псевдоэлемент ::shadow устарел и удаляется из профиля динамического селектора. Приведенный ниже подход требует, чтобы он оставался в статическом селекторном профиле, но в будущем он также может быть устаревшим и удаленным. Обсуждения продолжаются.

Мы можем использовать метод Element .matches(), чтобы определить, прикреплен ли элемент к теневому DOM.

Если и только если элемент находится в тени DOM, мы сможем сопоставить его, используя селектор :host, чтобы идентифицировать элементы, которые имеют Shadow DOM, ::shadow для просмотра в этих теневых DOM, и * и соответствовать любому потомству.

function isInShadow(element) {
  return element.matches(':host::shadow *');
}

function isInShadow(element) {
  return element.matches(':host::shadow *');
}

console.group('Testing');

var lightElement = document.querySelector('div');    

console.assert(isInShadow(lightElement) === false);

var shadowChild = document.createElement('div');
lightElement.createShadowRoot().appendChild(shadowChild);

console.assert(isInShadow(shadowChild) === true);

var orphanedElement = document.createElement('div');

console.assert(isInShadow(orphanedElement) === false);

var orphanedShadowChild = document.createElement('div');
orphanedElement.createShadowRoot().appendChild(orphanedShadowChild);

console.assert(isInShadow(orphanedShadowChild) === true);

var fragmentChild = document.createElement('div');
document.createDocumentFragment().appendChild(fragmentChild);

console.assert(isInShadow(fragmentChild) === false);

console.log('Complete.');
console.groupEnd();
<div></div>

Ответ 3

Вы можете проверить, имеет ли элемент теневой родительский элемент:

function hasShadowParent(element) {
    while(element.parentNode && (element = element.parentNode)){
        if(element instanceof ShadowRoot){
            return true;
        }
    }
    return false;
}

Используется instanceof над .toString().

Ответ 4

Позволяет понять Light Dom:

Light DOM - это предоставленный пользователем DOM элемента, в котором размещен теневой корень. Подробнее читайте в проекте "Полимер".

https://www.polymer-project.org/platform/shadow-dom.html#shadow-dom-subtrees

Это означает: Light DOM всегда относительный для следующего предка, в котором размещается теневой корень.

Элемент может быть частью легкого dom пользовательского элемента, тогда как он может быть частью теневого корня другого настраиваемого элемента в то же время.

Пример:

<my-custom-element>
    <shadowRoot>

        <custom-element>
            <div>I'm in Light DOM of "custom-element" and 
                    in Shadow Root of "my-custom-element" at same time</div>
        </custom-element>

    </shadowRoot>

    <div id="LDofMCE"> Im in Light DOM of "my-custom-element"</div>

<my-custom-element>

По вашему вопросу:

Если вы хотите узнать, находится ли элемент в теневом корневом, вам просто нужно извлечь элемент из документа .

var isInLD = document.contains(NodeRef);
if(isInLD){
    console.alert('Element is in the only available "global Light DOM"(document)');
} else {
    console.log('Element is hidden in the shadow dom of some element');
}

Единственный Light DOM, который не предшествует теневому корню, является частью документа, потому что Light DOM является относительным, как показано выше.

Он не работает в обратном направлении: если его часть документа отсутствует в Light DOM вообще. Вам нужно проверить, поддерживает ли один из предков теневой корень, предложенный Лео.

Вы можете использовать этот подход для других элементов. Просто замените "документ", например. "my-custom-element" и проверьте, если div#LDofMCE находится в Light DOM относительно "my-custom-element".

Из-за отсутствия информации о , почему вам нужна эта информация, я не могу приблизиться...

EDIT:

Он не работает в обратном направлении должен понимать следующее:

Этот элемент в корне тени?: document.contains() или метод isInShadow (node) от Leo доставляют ответ.

"назад" Вопрос: Этот элемент в Light DOM (в случае, если вы начинаете поиск по отношению к документу)?: domcument.contains() не доставляет ответа, потому что быть в свете Dom - один из элементов предков должен быть теневым хостом.

Приходите к точке

  • Светлый DOM относительный.
  • Элемент может принимать участие в теневом корне и в свете dom одновременно. нет "является частью тени DOM ИЛИ светлой DOM?"

Ответ 5

Я просто использую:

const isInShadow = node = !!node.shadowRoot;