Re: Mutation events replacement

On Thu, Jun 4, 2009 at 12:07 PM, Jonas Sicking <jonas@sicking.cc> wrote:

> Here's an API that might work:
>
> The following methods are added to the Document, Element and
> DocumentFragment interfaces:
>
>  addAttributeChangedListener(NodeDataCallback);
>  addSubtreeAttributeChangedListener(NodeDataCallback);
>  addChildlistChangedListener(NodeDataCallback);
>  addSubtreeChangedListener(NodeDataCallback);


I would go for
addSubtreeElementChangedListener for this one since a modification to an
attribute in the subtree does not trigger it


>
>  addTextDataChangedListener(NodeDataCallback);
>  removeAttributeChangedListener(NodeDataCallback);
>  removeSubtreeAttributeChangedListener(NodeDataCallback);
>  removeChildlistChangedListener(NodeDataCallback);
>  removeSubtreeChangedListener(NodeDataCallback);


and removeSubtreeElementChangedListener...


>
>  removeTextDataChangedListener(NodeDataCallback);
>
> And add the following interface:
> [CallBack=FunctionOnly, NoInterfaceObject]
> interface NodeDataCallback {
>  void handleEvent(in Node node);
> };


>
> These are *not* DOM-Event listeners. No DOM Events are created, there
> are no capture phases or bubbling phases. Instead you register a
> listener on the node you are interested in being notified about, and
> will get a call after a mutation takes place. The listeners are called
> as follows:
>
> 'AttributeChanged': Called when an attribute on the node is changed.
> This can be either a attribute addition, removal, or change in value.
> If setAttribute is called the 'AttributeChanged' listener is always
> called, even if the attribute is given the same value as it already
> had. This is because it may be expensive to compare if the old and new
> value was the same, if the value is internally stored in a parsed
> form.
>
> 'SubtreeAttributeChanged': As for 'AttributeChanged', but called not
> just for attribute changes to the node, but also to any descendants of
> the node.
>
> 'ChildlistChanged': Called if one or or mode children are added or
> removed to the node. So for example when innerHTML is set, replacing
> 10 existing nodes with 20 new ones, only one call is made to the
> 'ChildlistChanged' listeners on that node.
>
> 'SubtreeChanged': Same as 'ChildlistChanged', but called not just for
> children added/removed to the node, but also to any descendants of the
> node.
>
> 'TextDataChanged': Called when the data in a Text, CDATASection or
> ProcessingInstruction, is changed on the node or any of its
> decendants.
>
> The exact algorithm would go something like this:
>
> The implementation contains a global list, pendingCallbacks. Each item
> in pendingCallbacks is a tuple: <callback, node>.
> The implementation also contains a flag, notifyingCallbacks, which
> initially is set to false.
>
> If an attribute is changed on node N, perform the following steps:
>
> 1. For each 'AttributeChanged' callback, C, registered on N, add an
> item to the end of pendingCallbacks with the value <C, N>
> 2. For each 'SubtreeAttributeChanged' callback, C, registered on N,
> add an item to the end of pendingCallbacks with the value <C, N>
> 3. Set N to N's parentNode.
> 4. If N is non-null, go to step 2.
> 5. If notifyingCallbacks is set to true, or pendingCallbacks is empty,
> abort these steps.
> 6. Set notifyingCallbacks to true.
> 7. Remove the first tuple from pendingCallbacks.
> 8. Call the handleEvent function on the callback in the tuple, with
> the 'node' argument set to the node from the tuple.
> 9. If pendingCallbacks is not empty, go to step 7.
> 10 Set notifyingCallbacks to false.
>
> A similar algorithm is run when children are added and/or removed from
> an element/document/document-fragment, just replace 'AttributeChanged'
> with 'ChildlistChanged', and 'SubtreeAttributeChanged', with
> 'SubtreeChanged'.
>
> And when Text/CDATASection/ProcessingInstruction


Please add "Comment" too


> nodes have their
> contents modified the same algorithm is run, but without step 1, and
> with 'SubtreeAttributeChanged' replaced with 'TextDataChanged'.
>
> There's a few of properties here that are desirable, and one that is
> sub-optimal.
>
> First of all no notifications are done until after the implementation
> is done with the mutations. This significantly simplifies the
> implementation since all critical work can be done without worrying
> about script performing arbitrary operations in the middle.
>
> Second, all callbacks are notified in the order mutations happen in
> the tree, even when another callback performs further mutations. So if
> a callback for 'ChildlistChanged' sets a attribute on the newly
> inserted node, the other callbacks are still going to get first
> notified about the node being inserted, and then about it having an
> attribute set. This is thanks to the notifyingCallbacks flag.
>
> Third, these notifications should be significantly faster. Since there
> are no DOM Events, there is no need to build propagation chains.
> There's also no need to recreated the old attribute value, or to fire
> one event for each node if multiple nodes are added and removed.
>
> The only undesirable feature is that code that mutates the DOM from
> inside a callback, say by calling setAttribute, can't rely on that by
> the time that setAttribute returns, all callbacks have been notified.
> This is unfortunately required if we want the second desirable
> property listed above.


Indeed...


>
>
>
> The only feature of mutation events that this doesn't provide is the
> functionality that the
> DOMNodeRemovedFromDocument/DOMNodeInsertedIntoDocument. I believe that
> these are implemented in some browsers. I don't know how these
> browsers would feel about dropping support without supplying some sort
> of alternative. If needed we could add the following
>
>  addInDocumentChangedDocumentListener(NodeDataCallback);
>  removeInDocumentChangedDocumentListener(NodeDataCallback);
>
> But I'm not sure that there is web-dependency on this, or good use cases.
>
> / Jonas
>
> Xmlizer

Received on Thursday, 4 June 2009 11:37:47 UTC