W3C home > Mailing lists > Public > www-dom@w3.org > April to June 2009

Re: Mutation events replacement

From: Giovanni Campagna <scampa.giovanni@gmail.com>
Date: Sat, 6 Jun 2009 14:04:02 +0200
Message-ID: <65307430906060504y7505f9e5mfac34e8a18374a73@mail.gmail.com>
To: Jonas Sicking <jonas@sicking.cc>
Cc: www-dom@w3.org
2009/6/5 Jonas Sicking <jonas@sicking.cc>:
> On Fri, Jun 5, 2009 at 9:03 AM, Giovanni Campagna
> <scampa.giovanni@gmail.com> wrote:
>>>> This way we don't have to create a function for every node type (any,
>>>> attribute, element, chardata), multiplied by every node operation
>>>> (add, remove, change), multiplied by 2 (current node only or all
>>>> subtree) and again by 2 (add and remove handler) and thus we
>>>> disambiguate between different dom operations in fast native code,
>>>> rather than in the slow JS interpreter.
>>>
>>> I'm not quite following you here. Can you provide an example of a use
>>> case and code pattern that would be problematic?
>>
>> Just compare your list of methods and mine, then consider that yours
>> is incomplete:you need a new operation if you want to keep track of a
>> new modification, let it be a new node type in XML2/DOM4Core or a new
>> modification like EntityReferenceLocationChanged
>> (EntityReference.systemId) or ElementTypeInfoRedefined
>> (Element.schemaTypeInfo), mine instead is complete. In particular, you
>> need 4 new entries in the property table for every extension (add
>> current, add subtree, remove current, remove subtree), I need just one
>> integer value.
>> Adding specific methods, that may be overridden at JS level, is way
>> more burdensome than adding just two generic.
>
> Given that since the dawn of time for DOM support in browsers, not a
> single new modification types have been added. So I'm not too worried
> about having a large enough new number that this turns into a problem.

That's because DOM mutation events are slow and everybody would like
to deprecate them, instead than extending or encouraging their use.

>> On the author side, I don't find it very different to write
>> el.addElementSubtreeChangedListener(function() {})
>> than
>> el.addModificationListener(el.ElementListChanged,function() {},true)
>> or even
>> el.addModListener(el.SubElementMod,function() { },true);
>> (using compact names)
>>
>> and personally, I prefer the latter option
>
> I agree, it's not a big difference. It probably comes down to personal
> preference.
>
> I don't have a strong opinion. And given that this is just a syntactic
> difference I don't think we need to make a decision at this point.

That's ok then.

>>>> Lastly, you could use the event queue mechanism used by HTML5 for
>>>> asynchronous events (modifications callbacks are just events that are
>>>> not captured and neither bubble), instead of defining your own.
>>>
>>> I think we want the notifications to fire by the time that the
>>> function that caused the DOM mutation returned. So in code like
>>>
>>> myNode.innerHTML = "<b>hello world!</b>";
>>> doStuff();
>>>
>>> by the time doStuff is called all the notifactions have fired and
>>> updated whatever state that they need to update.
>>
>> So actually modification handler are syncronous.
>> In that case, we may need something like .startTransaction() and
>> .endTransaction()
>> 1) to batch modifications for author code
>> 2) to lock author code from unexpected modifications
>
> Why? And could you please explain 2 in more detail, i don't quite
> follow what you mean.

1) I set an attribute, the listener fires
I set the attribute again back to the previous value, the listener fires again
In that case, I may want to ignore the listener completely, because
the attribute didn't really change.

2) I have a long list of element that I want to add to another one.
Every appendChild() will trigger the modification listener, again and again.
In this case, I may want to use the "batch" feature, and call it just
once, in particular if the listener is watching only for the elements
present under the subtree, rather than the modification itself.

3) I'm sure there is code that expects (or requires) that after a
"setAttribute" the only thing that changed in the DOM was the actual
attribute set.
Preventing modification listeners within "protected" code helps this.

>> In my first interpretation, I was confused by this sentence:
>>> 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.
>> and I don't see why we need a global callback list, rather than one
>> local to the "modification batch list"
>
> Two of the goals that I had with my design was:
> 1. Listeners should be notified in the order that mutations take
> place. So for example if a node is first inserted into the DOM, and
> then an attribute is set on the node, the notifications should happen
> in that order, and never ever be notified first about the attribute
> change, and then about the node insertion.
>
> This goal is not fulfilled by DOM Mutation Events. Consider the following:
>
> 1. Two event listeners are registered for the DOMNodeInserted and one
> for DOMAttrModified
> 2. A node is inserted.
> 3. The implementation fires a DOMNodeInserted.
> 4. The first DOMNodeInserted listener fires. It sets an attribute on
> the inserted node.
> 4a. The implementation fires DOMAttrModified.
> 4b. The DOMAttrModified listener is notified.
> 5. The second DOMNodeInserted listener fires.
>
> As you see the, second DOMNodeInserted listener is notified after the
> DOMAttrModified listener. This is what I wanted to avoid.
>
> 2. Notifications should be synchronous. If someone calls
> .setAttribute(...), and there is a listener that updates a bunch of
> state based on the "attribute has changed" mutation notification, that
> the caller of setAttribute can rely on that all state is up to date by
> the time the setAttribute(...) returns.
>
> Unfortunately these two goals are incompatible. I can't fully satisfy
> them both. So the sacrifice I made was that calls that happen inside
> of a notification no longer satisfy the second goal. This is
> accomplished by the flag and queue described in the algorithm.
>
> I hope that makes sense?

It makes a lot of sense, sure. Just I find more intuitive the
reentrant behavior (modifications use local list), and I think that
both are possible and have some advantages.
Actually, I haven't understood yet why feature 1) is so important.

> / Jonas
>

Giovanni
Received on Saturday, 6 June 2009 12:04:40 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Friday, 22 June 2012 06:14:00 GMT