slotchange event doesn't fire when setting innerHTML
Reported by
j...@component.kitchen,
Feb 27 2017
|
|||||||||
Issue descriptionUserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 Steps to reproduce the problem: 1. Open the attached file or http://jsbin.com/ralosu/edit?html,console,output. 2. Inspect the console. What is the expected behavior? The console should log a slotchange for the two instances of the test custom element. WebKit handles this correctly. What went wrong? Blink logs a slotchange event for the first element, which was created statically in markup. However, when the second element is created by setting innerHTML, Blink does *not* fire the slotchange event as expected. Did this work before? N/A Does this work in other browsers? Yes Chrome version: 56.0.2924.87 Channel: stable OS Version: OS X 10.12.4 Flash Version: Many of our web components need to perform some initial processing of their contents. The inability to depend on slotchange for this in Blink is both surprising, and would significantly complicate our component code. This bug is distinct from the possibly related bug https://bugs.chromium.org/p/chromium/issues/detail?id=658605, which has been fixed.
,
Mar 2 2017
Tested in chrome # 56.0.2924.87 and Canary #58.0.3027.1 on Mac 10.12.3 and able to reproduce the issue but facing below error in older versions. @ jan: Could you please let me know if i have missed anything and if possible, provide us with a valid JS URL of the issue which would help us to triage the issue further. ccing 658605 dev team; @hayato : Could you please take a look in to this issue.This is similar to https://bugs.chromium.org/p/chromium/issues/detail?id=658605 Thanks in Advance.
,
Mar 2 2017
Thank you for reporting. I'll take a look.
,
Mar 2 2017
@rbasuvula You're asking for a URL "of the issue", but I'm not exactly sure what you meant. As you saw, I've already provided a jsbin that reproduces the issue. Please let me know if there is something else I can give you to help.
,
Mar 10 2017
@jan, I could reproduce this. I think we have enough information. Let me investigate further.
,
Mar 13 2017
I have investigated the behavior. The followings are happening here.
* 1st case: <test-element>one</test-element>
1. The parser encounter's an opening <test-element>
2. <test-element>'s constructor is called
- <test-element>'s shadowRoot.setInnerHTML("Hello, <slot></slot>.") is called.
- <slot> is inserted into <test-element>'s shadow root.
3. <test-element> is inserted into the document, as well as its shadow root (and the slot as well).
- Note that text node("one") has not been inserted into the document yet.
4. The parser encounters a text node ("one"), as a child of <test-element>.
5. text node ("one") is inserted into the document.
- Since <test-element>'s shadow root has already <slot> element, a slotchange event is enqueued.
* 2nd case: <div id=fixture>'s setInnerHTML("<test-element>two</test-element>")
1. <div id=fixture>'s setInnerHTML("<test-element>two</test-element>") is called.
2. The parser parses "<test-element>two</test-element>"
1. <test-element> is inserted into the (temporary) document fragment, which is used in parsing setInnerHTML's HTML.
Note that <test-element>'s constructor is NOT called at this timing. Thus, <test-element>'s shadow tree is not created yet.
2. A text node (two) is inserted into the temporary document fragment
3. The parsed <test-element>two</test-element> is inserted into the document, as a child of <div id=fixture></div>
4. <test-element>'s constructor is called at this timing. That means:
- <test-element> already has text node("two"), as a child node.
- <slot> is created and inserted into the shadow root of <test-element> at this timing. Thus, slotchange event does not happen.
Given that, from the slotchange event's perspective, this behavior is spec compliant.
I think the point is when <test-element>'s constructor should be called in the 2nd case (setInnerHTML case); Before it is inserted into the document, or after it is inserted into the document?
Let me add CustomElement label.
,
Mar 14 2017
dominicc@, domenic@
Could you help me to figure out when a custom element's constructor should be called if elementInDocumentTree.innerHTML('<custom-element>child-text-node</custom-element>' is used?
,
Mar 23 2017
Your analysis seems correct. The HTML parser is invoked for the purposes of the HTML fragment parsing algorithm, so https://html.spec.whatwg.org/#create-an-element-for-the-token sets "will execute script" to false. Thus https://dom.spec.whatwg.org/#concept-create-element gets invoked with the "synchronous custom elements flag" unset. Thus we end up in step 6.2 and enqueue a custom element upgrade reaction. That upgrade reaction will run immediately before returning from the innerHTML setter, but that is after the element has already been inserted into the document. It would be good to add a web platform test asserting that Blink's behavior is correct, so we can help WebKit be correct too.
,
Mar 23 2017
Thanks. I think we can close this bug. Let me write a web platform test.
,
Mar 23 2017
Blink's current behavior may comply with the spec — but before putting this aside, I did want to share that, from my perspective as a web component developer, the spec'ed behavior is surprising and feels unhelpful. Most of our components need to process their content — that, is perform some work on the nodes assigned to their slots. They need to do that processing with any initial content, as well as any new content that comes in later. WebKit (and, for what it's worth, the polyfill) give me a simple model for this: I wire up a slotchange handler. This fires once with the initial content, so I can use it to process the initial content, then fires when content changes. The Blink behavior means there exists at least one reasonably important edge case in which we cannot rely on slotchange. I'm not sure what the best pattern would be for processing component content, but wouldn't any other pattern be more complex? We could check initial content in connectedCallback, but we'd have to do that in conjunction with slotchange, and I'm not sure how we'd ensure that we only process changes once.
,
Mar 24 2017
jan@, I totally understand your opinion. I remember that we have decided this spec without much feedback from web component developers. You might want be interested in https://github.com/whatwg/dom/pull/229#issuecomment-211742359 where we have decided the current behavior; When slots are firstly inserted into a document, or removed from a document, we suppress a signal. That means slotchange does not happen in this case. If I remember correctly, only the feedback from web a developer about this behavior is one from a folk in Polymer. They do not care this behavior. I think it is worth considering changing the behavior. Could you file a bug for DOM Standard if you are interested in?
,
May 15 2017
It looks like there's already a spec discussion underway to change this: https://github.com/whatwg/dom/issues/447.
,
Jul 19 2017
For the record, it appears this issue has now been fixed (in Chrome Canary).
,
Jul 20 2017
Yeah, you are right. I forgot to update this issue. Thank you for pointing it out! For the record: 1. I've updated the DOM Standard: https://github.com/whatwg/dom/commit/8f8c1c30c39be38480336e7fc39c1cc7b40b6a83 2. I've updated Blink's behavior too: https://chromium-review.googlesource.com/c/532734/
,
Jul 20 2017
|
|||||||||
►
Sign in to add a comment |
|||||||||
Comment 1 by manoranj...@chromium.org
, Feb 27 2017