Понимание порядка обработки пользовательских элементов - PullRequest
1 голос
/ 28 марта 2020

Существует момент времени, когда родительский пользовательский элемент может получить доступ к своим дочерним элементам, прежде чем они будут благословлены своими пользовательскими методами.

class CustomParent extends HTMLElement {
  connectedCallback() {
    // works
    this.children[0].textContent = "bar";

    // works
    setTimeout(() => this.children[0].test(), 0);

    // throws a Type error
    this.children[0].test();
  }
}

customElements.define("custom-parent", CustomParent);


class CustomChild extends HTMLElement {
  test() {
    this.textContent = "baz";
  }
}

customElements.define("custom-child", CustomChild);

document.body.innerHTML = `
<custom-parent>
  <custom-child>foo</custom-child>  
</custom-parent>
`;

Как это возможно и безопасно ли откладывать this.children[0].test()

1 Ответ

2 голосов
/ 29 марта 2020

Это из-за процесса обновления пользовательского элемента .

1-й шаг : при выполнении document.body.innerHTML = '<custom-parent><custom-child>foo</custom-child></custom-parent>' 2 элемента вставляется на страницу как неизвестные элементы .

2-й шаг : родительский элемент обновлен первым. Он может получить доступ к своему дочернему элементу (а затем обновить свое свойство textContent) как элемент unknown . Но он не может получить доступ к методу пользовательского элемента test() ... потому что это еще не пользовательский элемент!

3-й шаг : дочерний элемент обновлен сразу после, и теперь получает метод test().

4-й шаг : отложенный вызов test() логически работает :-)

Смотрите пример ниже. Он использует querySelectorAll( ':not(:defined)' ), чтобы показать, что ребенок обновлен после своего родителя.

class CustomParent extends HTMLElement {
  constructor() { super() ; console.log( 'parent upgraded') }
  connectedCallback() {
    console.log( 'parent connected', this.children[0].outerHTML )
    // works
    this.children[0].textContent = 'bar'    
    // works
    setTimeout( () => this.children[0].test() )
    // throws a Type error
    try { 
       this.children[0].test() 
    } catch ( e ) { 
      //this shows the parent is upgraded, but not its child 
      var not_upgraded = document.querySelectorAll( ':not(:defined)' )
      console.log( 'not upgraded: ', ...not_upgraded )    
    }    
  }
}

customElements.define( 'custom-parent', CustomParent )

class CustomChild extends HTMLElement {
  constructor() { super() ; console.log( 'child upgraded') }      
  test() { this.textContent = 'baz' }
}

customElements.define( 'custom-child', CustomChild ) 

document.body.innerHTML = `
  <custom-parent>
    <custom-child>foo</custom-child>  
  </custom-parent>`
...