- From: Jonas Sicking <jonas@sicking.cc>
- Date: Thu, 17 Jul 2008 17:51:42 -0700
- To: Doug Schepers <schepers@w3.org>
- CC: "public-webapps@w3.org" <public-webapps@w3.org>
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. 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. 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 00:53:13 UTC