W3C home > Mailing lists > Public > public-webapps@w3.org > April to June 2011

Re: Mutation events replacement

From: Olli Pettay <Olli.Pettay@helsinki.fi>
Date: Thu, 30 Jun 2011 14:05:39 +0300
Message-ID: <4E0C5883.2090504@helsinki.fi>
To: Rafael Weinstein <rafaelw@google.com>
CC: Aryeh Gregor <Simetrical+w3c@gmail.com>, Adam Klein <adamk@google.com>, Jonas Sicking <jonas@sicking.cc>, Anne van Kesteren <annevk@opera.com>, Webapps WG <public-webapps@w3.org>
On 06/30/2011 12:54 AM, Rafael Weinstein wrote:
> On Wed, Jun 29, 2011 at 7:13 AM, Aryeh Gregor<Simetrical+w3c@gmail.com>  wrote:
>> On Tue, Jun 28, 2011 at 5:24 PM, Jonas Sicking<jonas@sicking.cc>  wrote:
>>> This new proposal solves both these by making all the modifications
>>> first, then firing all the events. Hence the implementation can
>>> separate implementing the mutating function from the code that sends
>>> out notifications.
>>>
>>> Conceptually, you simply queue all notifications in a queue as you're
>>> making modifications to the DOM, then right before returning from the
>>> function you insert a call like "flushAllPendingNotifications()". This
>>> way you don't have to care at all about what happens when those
>>> notifications fire.
>>
>> So when exactly are these notifications going to be fired?  In
>> particular, I hope non-DOM Core specifications are going to have
>> precise control over when they're fired.  For instance, execCommand()
>> will ideally want to do all its mutations at once and only then fire
>> the notifications (which I'm told is how WebKit currently works).  How
>> will this work spec-wise?  Will we have hooks to say things like
>> "remove a node but don't fire the notifications yet", and then have to
>> add an extra line someplace saying to fire all the notifications?
>> This could be awkward in some cases.  At least personally, I often say
>> things like "call insertNode(foo) on the range" in the middle of a
>> long algorithm, and I don't want magic happening at that point just
>> because DOM Range fires notifications before returning from
>> insertNode.
>>
>> Also, even if specs have precise control, I take it the idea is
>> authors won't, right?  If a library wants to implement some fancy
>> feature and be compatible with users of the library firing these
>> notifications, they'd really want to be able to control when
>> notifications are fired, just like specs want to.  In practice, the
>> only reason this isn't an issue with DOM mutation events is because
>> they can say "don't use them", and in fact people rarely do use them,
>> but that doesn't seem ideal -- it's just saying library authors
>> shouldn't bother to be robust.
>
> In working on Model Driven Views (http://code.google.com/p/mdv), we've
> run into exactly this problem, and have developed an approach we think
> is promising.
>
> The idea is to more or less take Jonas's proposal, but instead of
> firing callbacks immediately before the outer-most mutation returns,
> mutations are recorded for a given observer and handed to it as an
> in-order sequence at the "end" of the event.

What is the advantage comparing to Jonas' proposal?
I think one could implement your proposal on top
of Jonas' proposal - especially since both keep the order
of the mutations.
What is "at the 'end' of the event"? You're not talking about
DOM event here, but something else.
How is that different comparing to "immediately before the outer-most 
mutation"?

>
> var observer = window.createMutationObserver(callback);
Why is createMutationObserver needed?


> document.body.addSubtreeChangedObserver(observer);
> document.body.addSubtreeAttributeChangedObserver(observer);
> ...
> var div = document.createElement('div');
> document.body.appendChild(div);
> div.setAttribute('data-foo', 'bar');
> div.innerHTML = '<b>something</b>  <i>something else</i>';
> div.removeChild(div.childNodes[1]);
> ...
>
> // mutationList is an array, all the entries added to
> // |observer| during the preceding script event
> function callback(mutationList) {
> // mutationList === [
> //  { type: 'ChildlistChanged', target: document.body, inserted: [div] },
> //  { type: 'AttributeChanged', target: div, attrName: 'data-foo' },
> //  { type: 'ChildlistChanged', target: div, inserted: [b, i] },
> //  { type: 'ChildlistChanged', target: div, removed: [i] }
> // ];
> }
>
>>
>> Maybe this is a stupid question, since I'm not familiar at all with
>> the use-cases involved, but why can't we delay firing the
>> notifications until the event loop spins?  If we're already delaying
>> them such that there are no guarantees about what the DOM will look
>> like by the time they fire, it seems like delaying them further
>> shouldn't hurt the use-cases too much more.  And then we don't have to
>> put further effort into saying exactly when they fire for each method.
>
> Agreed.
>
> For context, after considering this issue, we've tentatively concluded
> a few things that don't seem to be widely agreed upon:
>
> 1) In terms of when to notify observers: Sync is too soon. Async (add
> a Task) is too late.
>
> - The same reasoning for why firing sync callbacks in the middle of
> DOM operations is problematic for C++ also applies to application
> script. Calling mutation observers synchronously can invalidate the
> assumptions of the code which is making the modifications. It's better
> to allow one bit of code to finish doing what it needs to and let
> mutation observers operate "later" over the changes.
>
> - Many uses of mutation events would actually *prefer* to not run sync
> because the "originating" code may be making multiple changes which
> more or less comprise a "transaction". For consistency and
> performance, the abstraction which is watching changes would like to
> operate on the final state.
>
> - However, typical uses of mutation events do want to operate more or
> less "in the same event" because they are trying to create a new
> consistent state. They'd like to run after the "application code" is
> finished, but before paint occurs or the next scheduled event runs.
>
> 2) Because the system must allow multiple "observers" and allow
> observers to make further modifications, it's possible for an
> arbitrary number of mutations to have occurred before any given
> observer is called. Thus is it preferable to simply record what
> happened, and provide the list to the observer.
>
> - An observer can be naive and assume that only "simple" mutations
> have occurred. However, it's more likely that an observer is an
> abstraction (like MDV) which only wants to do work WRT to the net of
> what has happened. This can be done by creating a projection which
> takes the sequence of mutations as input.
>
Received on Thursday, 30 June 2011 11:06:28 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 18:49:45 GMT