Repository navigation
Replace attached/detached callbacks with insertedIntoDocument/removedFromDocument #362
Description
Activity
F2F agreement: let's do this. Delta from the current spec:
attached->insertedIntoDocument;detached->removedFromDocument; remove the "and this document has a browsing context" clause.Order of callbacks should be root of tree (document element) to leaves, to be consistent with other callbacks.
Shadow DOM does not change the answer. If you are inserted into a shadow tree whose shadow root is not in document, you do not get called. When the shadow root gets inserted, you are called (in order).
F2F further discussion: nobody is as sure about the name anymore. If it's long it might need to be precise (insertedIntoDocumentDeeply or insertedIntoComposedDocument).
I'd still prefer having callback for ancestor chain changes and not for document changes, but I guess I can live without that.
During the meeting we claimed that "attach" and "detach" were unknowns, but note that we did introduce
attachShadowRoot(). Of course, its semantics are quite a bit different so those might still be bad words to use here.My preference here would be to use short names (e.g.,
insertedandremoved), since if we suffix them withCallbackthey'll be long enough. Developers will find out soon enough when they fire or simply read it in the standard.Regarding with the naming,
We should definitely avoid using
InsertedIntoDocument/removedFromDocument.
As we have decided not to extend the meaning of 'in a document' in #57, we should avoid re-usinginsertedIntoDocumentin another meaning here.It would be nice that the new name should be consistent with this issue #81, where
Node.inDocumentDeeplyis tentatively proposed. However, I do not like a term of 'in a document deeply' as a web-facing API name.My naive idea: How about using a term of connected? A term of connected is a reserved word in Web Standard?
So my preference
connectedIntoDocument/disconnectedFromDocument(If we prefer explicit long name)inserted/removed(If we prefer a short name, however, we still need a good name for node.isConnected #81)
I like
connectedToDocument/disconnectedFromDocumentbetter thaninserted/removedbecause it's more explicit especially if we wanted to add the latter two whenever any ancestor changes in the future.How do people feel about just
connected/disconnected?Looking at the history, and recalling from memory, seems there have been several changes here:
insertedCallback/removedCallbackenteredViewCallback/leftViewCallbackenteredDocumentCallback/leftDocumentCallbackattachedCallback/detachedCallbackconnected/disconnected?
I asked around at work and someone said:
imo, connected seems to mean that the 2 connected objects are more freely independent of each other. Whereas attached has a less flexible context.
like you'd say a leech was attached to your body, not connected to your body.
Others seemed to agree that
attachedanddetachedare the best fit.In terms of consistency:
inserted->Node.insertBefore()attached->HTMLElement.attachShadowRoot()disconnected->MutationObserver.disconnect()
The closest in terms of semantics seems to be
insertedandNode.insertBefore(). IIRCinsertedCallbackandremovedCallbackwere the original variations and were also used by Chrome's first implementation.I think the most important thing here is that names are descriptive, consistent with each other and don't change in the future. I'd vote for
attachedToDocumentanddetachedFromDocumentwhich leaves you open to useattachedToNodeanddetachedFromNodeif that behaviour is to be re-implemented in the future.Whatever happened to using symbols?
How do people feel about just
connected/disconnected?That doesn't tell us to what it's connected or from what it's disconnected.
That doesn't tell us to what it's connected or from what it's disconnected.
The idea would be that we would define in the spec new terms "connected" and "disconnected" which inherently have to do with being in a document.
If you're saying it doesn't tell readers of the code that, I think that's kind of true, but that will be true of any short and easy to type name. With my developer hat on, I'd personally rather pay the cost once of looking up "connected" if I get confused, than the cost of typing "connectedIntoDocumentCallback" every time I do this.
If you're saying it doesn't tell readers of the code that, I think that's kind of true, but that will be true of any short and easy to type name. With my developer hat on, I'd personally rather pay the cost once of looking up "connected" if I get confused, than the cost of typing "connectedIntoDocumentCallback" every time I do this.
I disagree. In practice, people would just copy & paste a template / name. I'd much rather have a specific name that communicates the meaning than an obnoxiously short name that doesn't tell us any semantics.
@domenic's and @rniwa’s points suggest that a good criteria by which to evaluate proposed callback names/semantics would be to consider an actual pair of concise, complete, and natural definitions for when those callbacks happen.
Such definitions are the sort of thing that are typically waved off for someone at MDN, etc., to work out when they write the high-level documentation aimed at a typical web developer (instead of someone who reads specs). But in cases like this, I think it's likely that the documentation will struggle to explain the concept accurately, and in a way that actually uses the callback names naturally in the explanation. If we could agree on a plausible natural-language definitions up front — words we ourselves might use to describe these callbacks to a developer friend, or expect to see on MDN — perhaps the callback names would fall out more easily.
I'll admit that it's really, really hard for me to reconstruct the current thinking by reading #57 and put these callbacks in that context. But based on my understanding, I'll put on my documentation writer hat and take a shot. Here's an initial draft for the "connected" callback, using that term provisionally to see how it feels:
connected: This callback is invoked when the custom element becomes part of the composed document. An element is in the composed document if it's directly in the document's light DOM, or in the Shadow DOM of a host element which is in the composed document. Generally speaking, an element in the composed document could be rendered. (Although there are reasons it might not be — the element might be styled to be hidden, for example.)
Is this definition in the right ballpark? I didn't see the term "composed document" in the linked discussion, but was wondering whether that term could serve as a proxy for "in a composed tree whose root is a Document".
Playing with natural-language definitions might lead to more confidence in the terms we pick. If we liked the above definition, for example, we might note that the definition doesn't use the term "connected", suggesting perhaps that the term is weaker than we'd like. In contrast, the word "composed" is used a lot, suggesting that a callback term including that word might be an interesting direction.
FYI, in #377, we are discussing how we should call the "in a document deeply". "in a composed document" is just proposed there.
We might want to have a consistent name with that if possible.
12 remaining items
For newAncestor, are you suggesting that
connectedCallback(document, newAncestor)? Or just for the purpose of passing arguments within the spec?That is what I'm suggesting, but maybe it is not necessary for a custom element to get that much context? I believe that is what browsers have internally, but I don't know offhand the nodes that require that information. There's a potential problem with that too, e.g., if newAncestor is inside a closed shadow tree we should probably not pass it…
@smaug---- pointed out document does not need to be passed, you can simply use
this.ownerDocument.defaultView. He also pointed out that in Gecko newAncestor/oldAncestor are only passed for the root (where they'd mean the same as newParent/oldParent) and not the descendants.- added a commit that references this issue
on Mar 7, 2016 I think I did this in 2d07c73. I ended up passing the document since to me it doesn't seem at all clear that the element's node document is equal to the parent node's containing document. Happy to fix if wrong.
See https://w3c.github.io/webcomponents/spec/custom/#custom-element-lifecycle for rendered output
You should not pass document. When a node is in a document or one of that document's hosted shadow trees, it's node document is that document. This will also be true for a node that was just removed.
Although I wonder how the timing works out if you
appendChilda node from a different document. Did you work that through?@annevk I don't understand. The document seems 100% necessary. Consider this case:
const p = document1.createElement("p"); const c = document1.createElement("custom-foo"); p.appendChild(c); document2.body.appendChild(p);
In this case
pis being inserted intodocument2.body.inclusiveDescendantisc. Andc's node document (i.e. the value ofthis.ownerDocumentinsideconnectedCallback) isdocument1. But we are connecting it todocument2. So we should be passingdocument2.There's no callback for the first
appendChild(), due to lack of a shadow-host-including root that is a document.For the second call to
appendChild(), by the time the callbacks are called,this.ownerDocumentwill bedocument2. This follows from the operationsappendChild()does.I see, I missed the pre-insertion steps. OK, we can remove the argument.
- added a commit that references this issue
on Mar 8, 2016 A little late, but why is
Callbackneeded in the name?The term "callback" is understood to be a function passed into another function, to be called in the future by some other code. In the years I've been using JavaScript, I never heard of class methods being called "callbacks" until I started using Custom Elements within the past year, and as user of the API I am never passing these methods anywhere, so they don't seem like "callbacks".
Maybe names like "whenConnected" and "whenDisconnected" would read better, and there's no misnomer there.
the
whenWhateverwould be misleading since there's already awhenDefinedwhich returns aPromiselike object which is incompatible with the semantics of these methods (connectedCallback/disconnectedCallback).I think these methods are just fine, and give room to libraries authors for different, enhanced
connect,disconnectoronConnect/Disconnectmethods.
Elements that define things or get used by other elements should probably do their work when they’re inserted into a document. e.g.
HTMLBaseElementneeds to modify the base URL of a document when it gets inserted. To support this use case, we need callbacks when an element is inserted into a document/shadow-tree and removed from a document/shadow-tree.Once we have added such callbacks, let us call them
insertedIntoDocumentandremovedFromDocumentcallbacks,attachedanddetachedcallbacks seems rather arbitrary and unnecessary as the author can easily check the existence of the browsing context viadocument.defaultView.