Re: [D3E] Possible Changes to Mutation Events

Doug Schepers wrote:
> 
> Hi, Jonas-
> 
> Thanks for this modified proposal.  I want to hear back from those 
> who've already commented as to their disposition, and to solicit 
> comments from other known implementors (e.g., gtk, BitFlash, Opera, 
> JSR), but I think your proposal is reasonable, and well detailed.
> 
> A few comments inline...
> 
> Jonas Sicking wrote (on 7/17/08 8:51 PM):
>>
>> * Add a |readonly attribute long relatedIndex;| property to
>>   the MutationEvent interface.
>> * Add a DOMChildRemoved event which is fired on a node when one
>>   of its children is removed. The relatedNode property contains the
>>   removed child, and relatedIndex contains the index the child had
>>   immediately before the removal. The event is fired after the removal
>>   takes place.
>> * Add a DOMDescendantRemovedFromDocument event which is fired on a node
>>   when the node is in a document, but any of nodes the descendants is
>>   removed from the document. The event is fired after the removal takes
>>   place.
>>   The relatedNode property contains the removed descendant. The
>>   relatedIndex property contains the index the child had
>>   immediately before the removal. (Should relatedIndex be -1 when
>>   the node wasn't removed from its parent, but rather an ancestor was?)
> 
> What is the rationale for having both 'DOMChildRemoved' and 
> 'DOMDescendantRemovedFromDocument'?  Wouldn't a single one, 
> 'DOMDescendantRemovedFromDocument' (or, preferably, 
> 'DOMDescendantRemoved'), work about as well?  You already give a way to 
> detect if it was a child or a descendant.

Well, I'd start by asking what the rationale is for mutation events at
all :) They seem to only solve the very simple cases where all parties
that mutate the page cooperate nicely with each other and with the
parties that listen to mutation events. But I would have expected that
in those cases the parties that mutate the DOM could instead have
notified the listening parties directly.

But I digress :)

Specifically the two new events correspond to the current DOMNodeRemoved
and DOMNodeRemovedFromDocument, so the rationale is the same as for
those old events. However I can only guess at that rationale is.

DOMNodeRemoved is useful when you want to know when a node is removed
from its parent.

DOMNodeRemovedFromDocument is useful when you want to know when a node
is no longer part of a document.

The latter seems in general more useful, for example for keeping a TOC
of headings in a page, or a list of clickable links. However the latter
seems very complicated to implement without severely regressing
performance any time there is a listener for the event. Whenever a node
is removed you have to fire an event for each and every node in the
removed subtree, presumably including attribute nodes.

Also DOMNodeRemoved can be mostly used to emulate
DOMNodeRemovedFromDocument by checking if the node you are interested in
(i.e. the link or the heading) has the removed node in its parent chain.

In mozilla we have never implemented DOMNodeRemovedFromDocument or
DOMNodeInsertedIntoDocument due to its high cost. Likewise I doubt that
we'll implement DOMDescendantRemovedFromDocument. I'm not sure what
other vendors have done about the old event or feel about the new.

> I understand that having the distinction means that you could filter on 
> the level of depth to fire events on, but I'm asking if this is useful 
> and necessary.

I take it you are asking under the general assumption that mutation
events are useful at all? :)

I generally think that DOMDescendantRemovedFromDocument is likely easier
to use, but seems prohibitively expensive to implement correctly,
whereas DOMChildRemoved seems to cover most use cases, though with a bit 
more effort on the side of the listener.

Also note that all these events have a capture phase, so you can always 
attack your listener on an ancestor of the nodes you are interested in, 
such as the document node.

>> * Specify *when* the events fire (see details below).
> 
> We should do this regardless, since it is tightening up the spec, not 
> changing it (though admittedly, it may force some implementations to 
> change anyway... but that means more interop).
> 
> 
>> * Deprecate the DOMNodeRemoved and DOMNodeRemovedFromDocument events.
>>   If this means making them optional or just discouraged I don't really
>>   care. I'd even be ok with simply leaving them in as is. Mozilla will
>>   simply remove our implementation of the DOMNodeRemoved event. We've
>>   never supported the DOMNodeRemovedFromDocument event.
> 
> If Mozilla is determined to remove them regardless of what the spec 
> says, then I would rather leave them in as specced, but deprecate them. 

Yes, given that someone pointed out that we can't really fire 
DOMNodeRemoved after the event takes place as that means that the parent 
chain is broken and so the event wouldn't reach any ancestors, I see no 
other alternative than dropping the event entirely.

Do note that there is still nothing that defines when these events 
should fire. I.e. if you do foo.textContent = '', does that fire a 
DOMNodeRemoved on all elements before any of them are removed, or does 
it fire the event on each of them as they are removed.

Though it might not be needed to be defined if the events get deprecated 
anyway...

>> As for when the events fire (note that this is just clarifications of 
>> the spec, not changes to it):
>> For events that fire after the mutation takes place I propose that we 
>> add a concept of a "compound operation" and state that while compound 
>> operations are in progress no mutation events are fired. Instead the 
>> events are queued up. After the outermost compound operation finishes, 
>> but before it returns control to the caller, the queue of mutation 
>> events is processed.
> 
> I really like the concept of the "compound operation", and I will add 
> that in regardless of other conclusions.  I will specify that a host 
> language should indicate when an operation is a "compound operation", 
> and describe the precise order of operations.  (See next comment for 
> details.)

Cool.

>> A compound operation may itself contain several compound operations. 
>> For example parent.replaceChild(newChild, oldChild) can consist of a 
>> removal (removing newChild from its old parent) a second removal 
>> (removing oldChild from parent) and an insertion (inserting newChild 
>> into its new parent). Processing of the queue wouldn't start until 
>> after the outermost compound operation finishes.
> 
> This actually has at least 3 variant orders of operation (assuming the 
> removal of newChild, which may not be in the document at all yet):
> 
> * remove newChild, remove oldChild, insert newChild
> * remove newChild, insert newChild, remove oldChild
> * remove oldChild, remove newChild, insert newChild
> 
> All of these are possible, and DOM3 Core doesn't indicate an order.  So 
> this adds to the unpredictability of dealing with any compound 
> operation, including mutation events.  Requiring a spec to state the 
> order would help authors.

Agreed.

/ Jonas

Received on Friday, 18 July 2008 18:54:33 UTC