-
Notifications
You must be signed in to change notification settings - Fork 376
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Presence of disconnectedCallback can expedite an invocation of connectedCallback #760
Comments
The problem comes down to step 3 of the concept to enqueue a custom element callback reaction exits early when there is no matching callback without enqueueing an element on the appropriate element queue. WebKit happens to have a bug to not skip the step to enqueue an element on the appropriate element queue even when there is no matching callback, and as a result doesn't exhibit this behavior. |
Rough consensus at TPAC F2F: Always enqueue a custom element regardless of whether it has a specific callback to avoid this changing of the behavior when a new callback is added. Action Item: Update the spec to fix this problem. |
@tomalec suggested that we also add an informal note saying |
Action Item(@rniwa): Make a PR for fixing WPT. |
I'm unassigning myself as I won't have time to work on this in the near future. I'd be happy to review a pull request, though. |
Use-case which needs a note/warning in spec or at least MDN https://jsbin.com/secexuq/edit?html,console,output class ParentElement extends HTMLElement {
connectedCallback() {
const child = this.childNodes[0];
log('Child removal begin');
this.removeChild(child);
log('Child removal end');
}
}
customElements.define('parent-element', ParentElement);
class Child1 extends HTMLElement {
connectedCallback() {
log('Child connectedCallback isConnected=' + this.isConnected.toString());
}
}
customElements.define('child-1', Child1);
const container = document.createElement('div');
container.innerHTML = '<parent-element><child-1></child-1></parent-element>';
customElements.upgrade(container);
document.body.appendChild(container);
// Child connectedCallback isConnected=false |
Tests: ... Fixes WICG/webcomponents#760.
Pulling my comment from the PR, I'm having second thoughts about this. If we made this change, presumably, all new reaction callback we would add should do the same. But that would mean that we would enqueue a custom element into the reaction queue at timings we currently don't do. That seems like a serious forward compatibility issue to me. Another somewhat different fix here might be that avoid invoking What do you all think? |
Thanks for the careful analysis; I think I agree.
The idea here would be, that we'd add something to https://html.spec.whatwg.org/#invoke-custom-element-reactions which is like "If the callback reaction's name is 'connectedCallback', and the element is not connected, skip this callback function"? I see the attraction. However, I'm unsure of the implications for custom element authors, and for the logic they put in their connectedCallback/disconnectedCallback. The element was briefly connected---even if it isn't right now. But the connected logic just gets ignored? Maybe these cases of "quickly connected and disconnected" shouldn't really count as being connected, in which case this change is fine. But I worry about introducing some other unexpected case, where authors are wondering why their callback isn't firing, and their important setup logic not happening. I don't have good intuition one way or the other here, and would love to hear more from others (especially authors). Also I wonder if you have similar thoughts about attributeChangedCallback? Like, if the attribute's current value is not equal to the newValue argument, should we avoid calling attributeChangedCallback? |
Right. That's definitely a risk here. But that kind of mutation analysis is probably best done with
I think |
I can't think of any use cases where this would cause any issues, especially since you'd theoretically also skip the disconnected callback which should undo any side effects in connected.
I haven't come across any use cases that this would break, but am unsure how comfortable I would be making this change. The word "changed" in the callback does imply that the value is different, though. Cheers for the mention. |
I'm personally perfectly fine with MutationObserver-like queuing of callbacks even if the underlying condition isn't met anymore - it seems ultimately simpler to explain if necessary and matches attributeChangedCallback currently. Let me ask around on my team though. |
Talking though it here, I'm also aware that this behavior could be weird to devs, so maybe skipping the callbacks is fine in this case too. @bicknellr might have more to say |
@rniwa I don't have objections in skipping callbacks if conditions aren't met, I would however argue developers should also be capable of understanding when "conditions" are met (i.e. the node is fully known, its tag was closed, the node is ready, all callbacks are trustable). |
Yeah, so the idea would be that if This way, when you receive connected or disconnected callbacks, you know that the element is currently connected or disconnected. One downside is that you wouldn't be able to tell whether the element had temporarily got connected / disconnected or not by simply listening to reaction callbacks. |
that works for me, I've never even expected these to be synchronous anyway |
So you cannot tell if the element was moved? (This means you cannot do |
I think in most cases the parent should be handling the relationship with the children and not the other way around. In the What is the Maybe there's room for something similar to the |
You can't do that though with today's technology, given that callbacks are triggered on the child, not the parent. If we wanted to change that, it would be pretty awkward to implement and spec; triggering callbacks on X when X gets connected/disconnected is easy, but having X's parent watch all of X's children (or descendants, in some cases!) requires a lot more machinery.
Right now moving is defined as removing and then adding, so introducing this distinction would be an unfortunate complication to the model. Overall I'm leaning back toward the status quo, just because it's more primitive and fundamental, and you can implement semantic batching on top of it. I'm also worried about implementing batching at this level for connect/disconnect but not attributeChanged, although as @rniwa points out it might not be observable in practice given the current set of DOM APIs. |
True. I think I'm spoiled by abstractions for this.
In retrospect, this is probably the safest option. |
To answer the earlier question, if you move an |
Yeah, the states quo might not be the worst behavior here. The currently spec'ed behavior is definitely weird but only when you start mutating DOM inside a custom element reaction, which is arguably already discouraged. I think we need to update MDN, etc... to make that discouragement more clear & prominent. |
… another element's connectedCallback https://bugs.webkit.org/show_bug.cgi?id=183586 <rdar://problem/38403504> Reviewed by Frédéric Wang. Source/WebCore: Align WebKit's behavior with Chrome/Firefox with regards to WICG/webcomponents#760 After much discussion, it's unclear that there is a clear path forward to fixing the oddness that the presence of a custom element reaction changes the timing at which another reaction callback gets invoked. So matching Chrome/Firefox behaviors in this case seems the path of the least resistance to interoperability. Namely, this patch makes WebKit not insert a custom element to the appropriate element queue when the element does not have a matching reaction callback. Put it another way, steps 3-5 in would be done before step 6 in: https://html.spec.whatwg.org/multipage/custom-elements.html#enqueue-a-custom-element-callback-reaction 1. Let definition be element's custom element definition. 2. Let callback be the value of the entry in definition's lifecycle callbacks with key callbackName. 3. If callback is null, then return 4. If callbackName is "attributeChangedCallback", then: 1. Let attributeName be the first element of args. 2. If definition's observed attributes does not contain attributeName, then return. 5. Add a new callback reaction to element's custom element reaction queue, with callback function callback and arguments args. 6. Enqueue an element on the appropriate element queue given element. Test: fast/custom-elements/enqueue-custom-element-callback-reactions-inside-another-callback.html * dom/CustomElementReactionQueue.cpp: (WebCore::CustomElementReactionQueue::enqueueElementUpgrade): (WebCore::CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded): (WebCore::CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded): (WebCore::CustomElementReactionQueue::enqueueAdoptedCallbackIfNeeded): (WebCore::CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded): (WebCore::CustomElementReactionQueue::enqueuePostUpgradeReactions): (WebCore::CustomElementReactionQueue::enqueueElementOnAppropriateElementQueue): Renamed from ensureCurrentQueue. * dom/CustomElementReactionQueue.h: LayoutTests: Added a W3C style testharness test. * fast/custom-elements/enqueue-custom-element-callback-reactions-inside-another-callback-expected.txt: Added. * fast/custom-elements/enqueue-custom-element-callback-reactions-inside-another-callback.html: Added. git-svn-id: http://svn.webkit.org/repository/webkit/trunk@239096 268f45cc-cd09-0410-ab3c-d52691b4dbfc
I've updated WebKit's implementation to match the status quo in https://trac.webkit.org/changeset/239096 |
Supersedes and closes #4127. Closes WICG/webcomponents#760.
Supersedes and closes #4127. Closes WICG/webcomponents#760.
Supersedes and closes #4127. Closes WICG/webcomponents#760.
Supersedes and closes whatwg#4127. Closes WICG/webcomponents#760.
Supersedes and closes whatwg#4127. Closes WICG/webcomponents#760.
Supersedes and closes whatwg#4127. Closes WICG/webcomponents#760.
… another element's connectedCallback https://bugs.webkit.org/show_bug.cgi?id=183586 <rdar://problem/38403504> Reviewed by Frédéric Wang. Source/WebCore: Align WebKit's behavior with Chrome/Firefox with regards to WICG/webcomponents#760 After much discussion, it's unclear that there is a clear path forward to fixing the oddness that the presence of a custom element reaction changes the timing at which another reaction callback gets invoked. So matching Chrome/Firefox behaviors in this case seems the path of the least resistance to interoperability. Namely, this patch makes WebKit not insert a custom element to the appropriate element queue when the element does not have a matching reaction callback. Put it another way, steps 3-5 in would be done before step 6 in: https://html.spec.whatwg.org/multipage/custom-elements.html#enqueue-a-custom-element-callback-reaction 1. Let definition be element's custom element definition. 2. Let callback be the value of the entry in definition's lifecycle callbacks with key callbackName. 3. If callback is null, then return 4. If callbackName is "attributeChangedCallback", then: 1. Let attributeName be the first element of args. 2. If definition's observed attributes does not contain attributeName, then return. 5. Add a new callback reaction to element's custom element reaction queue, with callback function callback and arguments args. 6. Enqueue an element on the appropriate element queue given element. Test: fast/custom-elements/enqueue-custom-element-callback-reactions-inside-another-callback.html * dom/CustomElementReactionQueue.cpp: (WebCore::CustomElementReactionQueue::enqueueElementUpgrade): (WebCore::CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded): (WebCore::CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded): (WebCore::CustomElementReactionQueue::enqueueAdoptedCallbackIfNeeded): (WebCore::CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded): (WebCore::CustomElementReactionQueue::enqueuePostUpgradeReactions): (WebCore::CustomElementReactionQueue::enqueueElementOnAppropriateElementQueue): Renamed from ensureCurrentQueue. * dom/CustomElementReactionQueue.h: LayoutTests: Added a W3C style testharness test. * fast/custom-elements/enqueue-custom-element-callback-reactions-inside-another-callback-expected.txt: Added. * fast/custom-elements/enqueue-custom-element-callback-reactions-inside-another-callback.html: Added. Canonical link: https://commits.webkit.org/207186@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@239096 268f45cc-cd09-0410-ab3c-d52691b4dbfc
When two custom elements are enqueued of connected callbacks, and the first
connectedCallback
removes the second one and inserts it back, whetherconnectedCallback
is invoked during the removal or the insertion depends on the presence ofdisconnectedCallback
.This seems like a very unintuitive behavior for developers. Why should adding a
disconnectedCallback
which doesn't do anything change the timing at whichconnectedCallback
is invoked?See https://gist.github.com/rniwa/537d2f7f29cc536f0731be1ff27258c8 for an example. In this example, the timing at which
connectedCallback
is invoked is different forChild1
which doesn't havedisconnectedCallback
andChild2
which does havedisconnectedCallback
.The text was updated successfully, but these errors were encountered: