Элемент доступа изнутри Shadow DOM - PullRequest
2 голосов
/ 06 февраля 2020

Я создал веб-компонент и хотел бы получить доступ к элементам из компонента.
Использую .attachMode ({mode: 'closed'}) таким образом, родитель не имеет доступа.

<template id='hello-world-template'>
  <span id='inside'>Unchanged,</span> <span id='outside'>Unchanged</span>
  <script>
    document.querySelector('#inside').innerHTML = 'Changed';
    // Ideal, but does not work - no such element 
  </script>
</template>
<hello-world></hello-world>

<script>
customElements.define('hello-world',
   class extends HTMLElement {
      constructor() {
         super();
         var template = document.getElementById('hello-world-template')
         var clone = template.content.cloneNode(true)
         const shadowRoot = this.attachShadow({mode: 'closed'}).appendChild(clone);
         }
      connectedCallback() {
        this.shadowRoot.querySelector('#outside').innerHTML = 'Changed';
        // Not ideal, and also does not work - this.shadowRoot has no querySelector
        }
      });
</script>

Некоторые попытки:

  1. Внутри фрагмента документа - это, self, window и document все ссылаются на родительское окно. И никто не может получить доступ к тени root.
  2. Попытка сохранить тень root в глобальной переменной и получить к ней доступ изнутри фрагмента или connectedCallback.
    Даже если это сработает, это приведет к поражению смысл использования {mode: 'closed'}, но в любом случае это не сработало.

У меня есть хак, который работает, но не могу себе представить, что я должен его использовать.
Весь смысл инкапсуляции в том, что вещи могут быть самодостаточными, но какая польза от этого, если JS не может воздействовать на другие предметы в своем контейнере?

Если это решение, хотелось бы дать подсказку, чтобы объяснить логика c компонентов пути, где они были реализованы.
Вот хак, однако: включите образ, который запускает JS onload.

<template id='hello-world-template'>
  <span id='inside'>Unchanged,</span> <span id='outside'>Unchanged</span>
  <script>
    function runner(img){
       let doc = img.parentNode;
       doc.querySelector('#inside').innerHTML = 'Changed';
       }
  </script>
  <img src='data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' onload="runner(this)">
</template>
<hello-world></hello-world>

Примечание относительно похожих вопросов ( 25048359 , 16633057 , 55101967 , et c.) - эти ответы не будут работать когда режим закрыт, что мне нужно.

1 Ответ

0 голосов
/ 06 февраля 2020

Похоже, у вас есть ошибки как в элементах, так и в this Область (в <script>)

const shadowRoot = this.attachShadow({mode: 'closed'}).appendChild(clone);

appendChild - это ваш Немезид здесь.

Возвращает вставленный элемент ... не тень- root, а #document-fragment (клонированный шаблон)

исправлено с:

 const shadowRoot = this.attachShadow({mode: 'closed'});
 shadowRoot.appendChild(clone);

Тогда:

  • mode:closed будет не назначать this.shadowRoot ..
    , которые вы можете не повторно использовать поскольку оно по-прежнему доступно только для чтения

с фиксированным значением:

 this.Root = this.attachShadow({mode: 'closed'});
 this.Root.appendChild(clone);

Теперь вы можете делать:

  connectedCallback() {
    this.Root.querySelector('#outside').innerHTML = 'Changed';
  }

Я не понимаю, почему вы считаете, что не идеально
this.Root доступно из всех / любых методов внутри Компонент

Хороший ресурс для всех DOM методы: https://javascript.info/modifying-document


<script> внутри <template> (эта область)

<template id='hello-world-template'>
  <span id='inside'>Unchanged</span>
  <script>
    document.querySelector('#inside').innerHTML = 'Changed';
    // Ideal, but does not work - no such element 
  </script>
</template>

Вы внедрили шаблон в компонент
document может не элементы доступа внутри любой компонент shadowDOM
делает не имеет значения, является ли shadowDOM mode:closed или mode:open

<script> область будет window (так как ему не была назначена область)

(я не думаю, что вы можете установить область действия this для сценария)

Чтобы получить область действия Component внутри этого <script>, вы должны быть творческими ..
и используйте ваш img onload= 'hack'

С onload на элементе <style> делает его немного менее взломанным (IMHO)

<template id='hello-world-template'>
  <span id='inside'>Inside Unchanged,</span>
  <script>
    function templFunc() {
      // this scope is the shadowRoot, not the component!
      this.querySelector('#inside').innerHTML = 'Changed';
    }
  </script>
  <style onload="templFunc.apply(this.getRootNode())">
    #inside{
      color:green;
    }
  </style>
</template>

один основная проблема: будет выполняться только для первого использованного <hello-world> элемента !!

Я не написал это из макушки головы,
(Работа в процессе) JSFiddle детская площадка (также показ ссылки на Компонент методы ) по адресу:
https://jsfiddle.net/CustomElementsExamples/zpamx728/

Обновление # 1

Хром (Edge / Chrome) ) и Opera в порядке, FireFox v72.0.2 плохо себя ведет:

  • onload на элементе <STYLE> будет срабатывать только для первого элемента
    Я изменил JSFiddle, чтобы использовать ваш первый взлом, используя <img>, и он запускается для каждого элемента

  • templFunc() загружается / ограничивается в shadowDOM, поэтому может вызываться из методов компонента (см. JSFiddle)
    , но .. только в FireFox НЕ определено / доступно для первого элемента
    На данный момент я считаю, что эта ошибка FireFox ... будет расследовать дальше ... (смело go где. ..)

!!! Обновление № 2 !!!

Упс! Поиграл еще с этим.

Оказывается, все переменные и функции из клонированного и импортированного сценария
подняты в глобальную window область действия

Так что приведенный выше код работает, но имеет основной побочный эффект ...

Вот почему FireFox жалуется на повторное объявление let переменных

...