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

Re: Mutation events replacement

From: Jonas Sicking <jonas@sicking.cc>
Date: Fri, 5 Jun 2009 14:58:54 -0700
Message-ID: <63df84f0906051458gdb65b8v68830288911d5959@mail.gmail.com>
To: Giovanni Campagna <scampa.giovanni@gmail.com>
Cc: mozer <xmlizer@gmail.com>, www-dom@w3.org
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.

> 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.

>>> 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.

> 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?

/ Jonas
Received on Friday, 5 June 2009 21:59:46 GMT

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