65927

Deferred setAttribute call in Custom Element constructor causes DOM error. Is it a bug?

<h3>Question</h3>

Here's a fiddle showing an error in console in Chrome 72 and Firefox 63:

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

The code is:

<pre class="snippet-code-html lang-html prettyprint-override"> <script> customElements.define('test-element', class extends HTMLElement { constructor() { super() Promise.resolve().then(() => { this.setAttribute('foo', 'bar') }) } }) </script> <test-element>test</test-element>

In Chrome the error is:

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

In Firefox the error is:

NotSupportedError: Operation is not supported

If you comment the setAttribute call, the error goes away in both browsers.

The following example illustrates changing attributes before an element is connected, which shows that it can be done with macrotasks, yet (unfairly) not with microtasks:

(working fiddle for snippet below)

<pre class="snippet-code-js lang-js prettyprint-override"> 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) })

In the first example I am not setting the attribute in the constructor, I am deferring to a future microtask. So why are the browsers complaining? If this is intended as per spec, then does the spec have a "design bug"? Why should we not be able to do this?

based on answers below, I don't see why this limitation <em>needs</em> to be in place. A bad developer can still make a mess with or without this browser-engine limitation in place.

IMO, let devs decide (and document) how their custom elements work.

Is there some technical limitation that the browser otherwise can't overcome if we were to be able to set attributes in the contructor or a microtask after the constructor?


<h3>Answer1:</h3>

According to the spec there are certain things you must never do in the constructor:

<blockquote>

When authoring custom element constructors, authors are bound by the following conformance requirements:

<ul><li>

A parameter-less call to super() must be the first statement in the constructor body, to establish the correct prototype chain and this value before any further code is run.

</li> <li>

A return statement must not appear anywhere inside the constructor body, unless it is a simple early-return (return or return this).

</li> <li>

The constructor must not use the document.write() or document.open() methods.

</li> <li>

The element's attributes and children must not be inspected, as in the non-upgrade case none will be present, and relying on upgrades makes the element less usable.

</li> <li>

The element must not gain any attributes or children, as this violates the expectations of consumers who use the createElement or createElementNS methods.

</li> <li>

In general, work should be deferred to connectedCallback as much as possible—especially work involving fetching resources or rendering. However, note that connectedCallback can be called more than once, so any initialization work that is truly one-time will need a guard to prevent it from running twice.

</li> <li>

In general, the constructor should be used to set up initial state and default values, and to set up event listeners and possibly a shadow root.

</li> </ul>

Several of these requirements are checked during element creation, either directly or indirectly, and failing to follow them will result in a custom element that cannot be instantiated by the parser or DOM APIs.

</blockquote>

The problem with your example is that the Promise is resolved immediately and is, thus, still in the constructor.

If you change your code to this:

<pre class="snippet-code-js lang-js prettyprint-override">customElements.define('test-element', class extends HTMLElement { constructor() { super() setTimeout(() => { this.setAttribute('foo', 'bar') }, 100) } }) <pre class="snippet-code-html lang-html prettyprint-override"><test-element>test</test-element>

Then it works because the setTimeout gets you out of the constructor.


<h3>Answer2:</h3>

The spec mentions this:

<blockquote>

This is true even if the work is done inside a constructor-initiated microtask, as a microtask checkpoint can occur immediately after construction.

</blockquote>

来源:https://stackoverflow.com/questions/54857905/deferred-setattribute-call-in-custom-element-constructor-causes-dom-error-is-it

Recommend

  • RPC with protocol buffers
  • Getting output buffer from DBMS_OUTPUT.GET_LINES in C#
  • multi column sorting of datagrid view:
  • DBCP Connection properties
  • I get a pg error when trying to destroy a controller
  • Remove alt-codes from string
  • Cannot import subprocess.call when running google app engine locally
  • Azure ARM DSC scale set deployment - cannot locate script
  • Updating price of in app purchase
  • Wordpress plugins it asks for FTP Details
  • java outlook send mail
  • How to split a string into a list by digits? [duplicate]
  • Django staticgenerator vs CACHE_BACKEND
  • Angular 6 illegal operation on a directory, open '/Users//.npm-global/lib/node_modules/
  • ASP.NET: replacing UpdatePanel with Jquery?
  • ASP.NET MVC 3 ListBox validation
  • Firebase suddenly reports invalid signature
  • Hyperlink to Outlook Attachment
  • Logout user after Woocommerce Checkout
  • How to manipulate content of a comment with Apache POI
  • How to smoothly connect two signals in matlab [closed]
  • Planned Contrasts on glmmTMB
  • Laravel 5 - Cache remember doesn't work
  • SELECT on JSONField with Django
  • Jekyll - How do I create pages in the root directory?
  • Pandas time series data Index from a string to float [duplicate]
  • Boolean filter using a timestamp value on a dataframe in Python
  • css: column-count 3, image floating spanning 2, chrome not playing. why?
  • readmore button or show/hide for php
  • How to use array in autohotkey?
  • LINQ to populate treeview based upon grouping
  • Generate and export point cloud from Project Tango
  • Annotate objects in a queryset with next and previous object ids
  • JavaScript Regex to Match Boundaries of Words with diacritics
  • How to turn off notice reporting in xampp?
  • Can a PHP script be scheduled to run at a specific time or after a specific amount of time has expir
  • reshape/remould data frame to create normalized bar chart and pie chart
  • Google App Engine Datastore: Dealing with eventual consistency
  • ssh remote server login script
  • media foundation H264 decoder not working properly