перемещение элемента из легкой DOM в ShadowDOM без отключения

Интересно, на правильном ли я здесь пути?

Цель: убедиться, что все элементы попадают в shadowDOM.

Итак, созданный вручную файл HTML

<cardts-pile>
  <cardts-card>A</cardts-card>
  <cardts-card>B</cardts-card>
</cardts-pile>

создает карточки в легкой модели документа <cardts-pile>

если я перенесу их в shadowDOM (конечно):

<cardts-card> удаляется из DOM (запускается disconnectedCallback())
<cardts-card> снова добавляется (запускается connectedCallback())

[см. console.log о Run Code Snipper ниже]

У меня есть более интересный код в card.connectedCallback()
при повторном подключении он в основном запускает тот же тот же код снова.

Вопросов

  1. Можно ли перемещать узлы без изменения DOM?

  2. Есть ли код OOTB, чтобы проверить, перемещается ли только существующий <cardts-card>,
    чтобы connectedCallback знал, что ему не нужно запускать код снова.

  3. Должен ли я сделать что-то другое,
    заставить эти элементы lightDOM немедленно оказаться в shadowDOM?

customElements.define('cardts-pile', class extends HTMLElement {
  constructor(){
    super();
    this.attachShadow({mode: 'open'}).innerHTML='<slot></slot>';
  }
  connectedCallback() {
    console.log('connect pile');
  }
});

customElements.define('cardts-card', class extends HTMLElement {
  constructor(){
    super();
    this.attachShadow({mode: 'open'}).innerHTML='<slot></slot>';
  }
  connectedCallback() {
    console.log('connect card',this.innerText);
    if (!this.getRootNode().host) // not in shadowDOM
       this.parentNode.shadowRoot.insertBefore(this,null);//or appendChild
  }
  disconnectedCallback() {
    console.log('disconnect card',this.innerText);
  }
});
<cardts-pile>
  <cardts-card>A</cardts-card>
  <cardts-card>B</cardts-card>
</cardts-pile>


person Danny '365CSI' Engelman    schedule 06.02.2019    source источник
comment
почему бы вам не использовать вместо этого событие смены слотов?   -  person Supersharp    schedule 07.02.2019
comment
[румянец] Потому что я не знал о _1 _.... При чтении документация по смене слотов Мне кажется, что это больше похоже на MutationObserver в слоте, не похоже, что это помешает disconnectedCallback()   -  person Danny '365CSI' Engelman    schedule 07.02.2019
comment
ты прав ... я не понял цель твоего вопроса   -  person Supersharp    schedule 07.02.2019


Ответы (1)


Можно ли перемещать узлы без изменения DOM?

Нет (насколько мне известно о Shadow DOM).

Есть ли код OOTB, чтобы проверить, перемещается ли только существующий?

Я бы использовал логический флаг:

connectedCallback() {
    if ( !this.connected )
        console.log( 'creation' )
    else {
        console.log( 'move' )
    this.connected = true   
}

(or in disconnectedCallack)

customElements.define('cardts-pile', class extends HTMLElement {
    constructor(){
        super();
        this.attachShadow({mode: 'open'}).innerHTML='<slot></slot>';
        this.shadowRoot.addEventListener( 'slotchange', ev => {      
            let node = this.querySelector( 'cardts-card' )
            node && this.shadowRoot.append( node )
        })
    }
    connectedCallback() {
        console.log('connect pile');
    }
});
    
customElements.define('cardts-card', class extends HTMLElement {
    constructor(){
        super();
        this.attachShadow({mode: 'open'}).innerHTML='<slot></slot>';
    }
    connectedCallback() {
        if ( !this.connected )
            console.log( this.innerText + ' created' )
        else 
            console.log( this.innerText + ' moved' )
        this.connected = true 
    }
    disconnectedCallback() {
        if ( !this.moved )
            console.log( 'moving ' + this.innerText );
        else 
            console.log( 'really disconnected' )
        this.moved = true
    }
});
<cardts-pile>
  <cardts-card>A</cardts-card>
  <cardts-card>B</cardts-card>
</cardts-pile>

Должен ли я делать что-то другое?

Вместо этого вы можете определить или обновить <cardts-card> только после перемещения элементов unknown, если возможно, хотя я не думаю, что это хорошая практика, если вы не можете контролировать время всего выполнения, например, с помощью whenDefined() или с помощью упорядоченного Код HTML и Javascript:

customElements.define('cardts-pile', Pile)
customElements.whenDefined('cardts-pile').then(() => 
    customElements.define('cardts-card', Card)
)

В приведенном ниже примере вы определяете класс Pile до или после класса Card (в зависимости от того, как они связаны).

class Card extends HTMLElement {
    constructor(){
        super()
        this.attachShadow({mode: 'open'}).innerHTML='<slot></slot>'
    }
    connectedCallback() {
        console.log(this.innerText + ' connected')
    }
    disconnectedCallback() {
         console.log(this.innerText + ' disconnected')
    }
}

class Pile extends HTMLElement {
    constructor() {
        super()
        this.attachShadow({mode: 'open'})
    }
    connectedCallback() {
        console.log('connect pile')
        this.shadowRoot.append(...this.querySelectorAll('cardts-card'))
    }
}

window.onload = () => customElements.define('cardts-pile', Pile)
customElements.whenDefined('cardts-pile').then(() => 
    customElements.define('cardts-card', Card)
)
<cardts-pile>
  <cardts-card>A</cardts-card>
  <cardts-card>B</cardts-card>
</cardts-pile>

person Supersharp    schedule 06.02.2019
comment
► 1. Вы мой герой на Elements / shadowDOM, поэтому ответ - НЕТ. ► 2. Нет необходимости в дополнительном флаге, !this.getRootNode().host ЯВЛЯЕТСЯ флагом ► 3. На первый взгляд (действительно, не очень хорошая практика, как вы сказали), я не могу использовать ваш пример кода, поскольку <cardts-pile> теперь зависит от (необходимо знать о ) <cardts-card> (есть и другие вещи, которые собираются в кучу). Я собираюсь продолжить с ►2◄ (пока оставим вопрос открытым для других ответов) - person Danny '365CSI' Engelman; 07.02.2019
comment
:-) на самом деле this.getRootNode.host может вернуть истину, если ваш компонент содержится в контейнере Shadow DOM (это зависит от того, контролируете ли вы глобальный макет страницы или собираетесь распространять ваше веб-приложение). Примечание: неясно, но мой пример кода иллюстрирует №2, а не №3. - person Supersharp; 07.02.2019