Re: [D3E] Possible Changes to Mutation Events

On Jul 17, 2008, at 5:51 PM, Jonas Sicking wrote:

> Doug Schepers wrote:
>> Jonas proposes two substantive changes to this:
>> * DOMNodeRemoved and DOMNodeRemovedFromDocument would be fired  
>> after the mutation rather than before
>> * DOM operations that perform multiple sub-operations (such as  
>> moving an element) would be dispatched (in order of operation)  
>> after all the sub-operations are complete.
> So based on the feedback so far in this thread, here is a revised  
> proposal from me, with the added feature that it's backwards  
> compatible
> with DOM Events Level 2:
> * 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?)
> * Specify *when* the events fire (see details below).
> * 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.

We'd probably follow suit in WebKit, in which case let's just remove  
the old events from the spec, or include them as an informative note  
for implementations that care to be compatible with Level 2.

However... in practice it seems that many non-browser-hosted  
implementations of DOM mutation events do not get the ordering right,  
and for browser-hosted implementations we'll break compatibility  
anyway, so I do not think there is any value to keeping nominal  
compatibility in the spec. Thus I would be equally ok with just  
changing the old events.

> 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.
> 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.
> One important detail here is that processing of the queue starts  
> *after* the compound operation finishes. This means that if a  
> mutation listener causes another mutation to happen, this is  
> considered a new compound operation. So if the mutation listener  
> causes another mutation to happen, then the mutation events queued  
> up during that compound operation fires before that compound  
> operation returns.
> There are two ways to implement this:
> Either you move the currently queued events off of the queue before  
> starting to process them.
> Or, when starting an outermost compound operation, remember what the  
> current length of the queue is, and when finishing the operation,  
> only process queued events added after that index.
> The whole point of this inner queuing is to allow mutations inside  
> mutation listeners to behave like mutations outside them. So if code  
> inside a mutation listeners calls .replaceChild, it can count on  
> that the mutation listeners for that mutation has fired by the time  
> replaceChild returns.

Your proposed behavior amounts to a stack of queues.

> What exactly constitutes a compound operation is left up to the  
> specs that describe the operations. For example setting .innerHTML  
> should likely constitute a compound operation. For DOM Core, every  
> function that mutates the DOM should be considered a compound  
> operation.
> This all does sound a bit complicated. However the same would be  
> true for any sufficiently detailed specification of how mutation  
> events behave. As stated, the current wording of the spec allows for  
> wildly different and useless firing logic.
> The only thing that we could somewhat simplify would be to not use  
> separate queues for mutations inside mutation listeners. However the  
> simplification would be pretty marginal, and I think it would be  
> confusing that mutations inside mutation listeners wouldn't cause  
> events to fire until after all other pending events had fired.
> / Jonas

Received on Friday, 18 July 2008 21:08:16 UTC