- From: Joe Pea <notifications@github.com>
- Date: Wed, 30 Jan 2019 10:58:25 -0800
- To: w3c/webcomponents <webcomponents@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <w3c/webcomponents/issues/787/459065552@github.com>
Alright, I thought about it, a workaround for using the `MutationObserver` approach and also being able to clean up on `disconnectedCallback` and re-observe on `connectedCallback`, is to: - create the observer in the `constructor` so that we get the benefit of it during parsing, - disconnect and dispose of it in `disconnectedCallback`, - and in `connectedCallback` create a new observer if one doesn't exist. So `connectedCallback` will handle cases of imperative manipulation of the elements (suppose they are cached and will be re-used by React's (or other lib's) internals), while the `constructor` will handle the parsing case. A **very big downside** of this approach is that an observer is created even if the node is outside of a tree, and like I mentioned above in the previous comment, I'd rather make it behave the way `connectedCallback` and `disconnectedCallback` do with respect to a those callback being called on a tree only when the tree is inserted into a document. This is bad because: - for a tree outside of a document, - `childConnectedCallback` will be called on parents, while `connectedCallback` will not be called on children. - This will cause drastically different behavior from nodes that are in a document, and will definitely be a source of bugs that require extra complexity to solve. The `CustomEvent` approach has the advantage that because it is implemented in `connectedCallback` and `disconnectedCallback`, it happens only when the tree is inserted into a document, which is 👍, so: - The `connectedCallback` of a child **always** corresponds one-to-one with a `childConnectedCallback` call in the parent. - Same with `disconnectedCallback` and `childDisconnectedCallback` - this prevents the above problem. So taking @rniwa's example, here's an updated version showing the `constructor` + `connectedCallback` approach. Look in the console to see the problem I just described, and you'll see that for the element which are not being placed into the document, the execution of code is imbalanced: ```html <!DOCTYPE html> <html> <body> <pre id="log"></pre> <script> let instanceCount = 0 customElements.define( 'test-element', class TestElement extends HTMLElement { constructor() { super() this.instanceNumber = ++instanceCount this.changeCount = 0 this.createObserver() } connectedCallback() { console.log('PARENT CONNECTED CALLBACK') this.style = 'display: block' this.createObserver() } disconnectedCallback() { this.destroyObserver() } childConnectedCallback(child) { console.log('CHILD CONNECTED CALLBACK') this.changeCount++ this.appendChild( document.createTextNode( `child changed! Change count: ${ this.changeCount }, Child tag: <${child.tagName.toLowerCase()}>, Child number: ${ child.instanceNumber }`, ), ) } createObserver() { if (this._observer) return this._observer = new MutationObserver(changes => { for (const change of changes) { change.type === 'childList' && change.addedNodes && Array.from(change.addedNodes).forEach(child => { if (!(child instanceof Element)) return this.childConnectedCallback(child) }) } }) this._observer.observe(this, { childList: true }) } destroyObserver() { if (!this._observer) return this._observer.disconnect() this._observer = null } }, ) setInterval(() => { document .querySelector('test-element') .appendChild(document.createElement('test-element')) console.log(' --- Test tree outside of the document:') // shows that the MutationObserver still operates on Nodes that are not in a document. :( const one = document.createElement('test-element') const two = document.createElement('test-element') const three = document.createElement('test-element') one.appendChild(two) two.appendChild(three) }, 1000) </script> <test-element> Test1 <test-element>Test2</test-element> <test-element>Test3</test-element> </test-element> </body> </html> ``` That's not a nice problem to have. The both APIs should be symmetrical! -- You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub: https://github.com/w3c/webcomponents/issues/787#issuecomment-459065552
Received on Wednesday, 30 January 2019 18:58:46 UTC