Отложенный вызов setAttribute в конструкторе Custom Element вызывает ошибку DOM.Это ошибка? - PullRequest
0 голосов
/ 25 февраля 2019

Вот скрипка, показывающая ошибку в консоли в Chrome 72 и Firefox 63:

https://jsfiddle.net/jr2z1ms3/1/

Код:

    <script>
    customElements.define('test-element', class extends HTMLElement {
      constructor() {
      	super()
      	Promise.resolve().then(() => {
        	this.setAttribute('foo', 'bar')
        })
      }
    })
    </script>
    <test-element>test</test-element>

В Chrome ошибка:

Uncaught DOMException: Failed to construct 'CustomElement': The result must not have attributes

В Firefox ошибка:

NotSupportedError: Operation is not supported

Если вы прокомментируете setAttribute вызов, ошибка исчезает в обоих браузерах.

В следующем примере показано изменение атрибутов перед подключением элемента, что показывает, что это можно сделать с помощью макрозадач, но (несправедливо) не с помощью микрозадач:

( рабочая скрипка для фрагмента ниже)

    customElements.define('test-element', class extends HTMLElement {
      constructor() {
        super()
        setTimeout(() => {
          this.setAttribute('foo', 'bar')
        })
      }
      
      connectedCallback() {
      	console.log('foo attribute:', this.getAttribute('foo'))
      }
    })
    
    const el = document.createElement('test-element')
    
    console.log('no foo attribute:', el.getAttribute('foo'))
    
    setTimeout(() => {
    	document.body.appendChild(el)
    })

В первом примере я не устанавливаю атрибут в конструкторе, я откладываю на будущую микрозадачу.Так почему же браузеры жалуются?Если это предусмотрено спецификацией, то есть ли в спецификации «ошибка проектирования»?Почему мы не должны быть в состоянии это сделать?

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

IMO, пусть разработчики решают (и документируют), как работают их пользовательские элементы.

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

Ответы [ 2 ]

0 голосов
/ 25 февраля 2019

Согласно спецификации есть определенные вещи, которые вы никогда не должны делать в конструкторе:

При разработке пользовательских конструкторов элементов авторы обязаны соблюдать следующие требования соответствия:

  • Безпараметрический вызов super () должен быть первым оператором в теле конструктора, чтобы установить правильную цепочку прототипов и это значение перед выполнением любого следующего кода.

  • Оператор возврата не должен появляться где-либо внутри тела конструктора, если только это не простой досрочный возврат (возврат или возврат этого).

  • Конструктор долженне используйте методы document.write () или document.open ().

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

  • Элемент не должен иметь никаких атрибутов или дочерних элементов , так как это нарушает expДействия потребителей, использующих методы createElement или createElementNS.

  • В общем, работа должна быть максимально отложена до connectedCallback, особенно работа, связанная со выборкой ресурсов или рендерингом.Тем не менее, обратите внимание, что connectedCallback можно вызывать более одного раза, поэтому любая работа по инициализации, которая действительно является одноразовой, потребует защиты, чтобы предотвратить ее выполнение дважды.

  • В общем случае конструкторследует использовать для установки начального состояния и значений по умолчанию, а также для настройки прослушивателей событий и, возможно, теневого корня.

Некоторые из этих требований проверяются во время создания элемента, прямо или косвеннои невыполнение их приведет к созданию пользовательского элемента, который не может быть создан при помощи синтаксического анализатора или API DOM.

Проблема с вашим примером состоит в том, что Promise решается немедленно и, таким образом,, все еще в конструкторе.

Если вы измените свой код на это:

customElements.define('test-element', class extends HTMLElement {
  constructor() {
    super()
    setTimeout(() => {
        this.setAttribute('foo', 'bar')
    }, 100)
  }
})
<test-element>test</test-element>

Тогда это работает, потому что setTimeout выводит вас из конструктора.

0 голосов
/ 25 февраля 2019

spec упоминает об этом:

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

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