Re: Mutation events replacement

On Thu, Jun 4, 2009 at 6:31 AM, Robin Berjon <robin@berjon.com> wrote:
> On Jun 4, 2009, at 12:14 , Jonas Sicking wrote:
>>
>> On Thu, Jun 4, 2009 at 3:56 AM, Robin Berjon <robin@berjon.com> wrote:
>>>
>>> On Jun 4, 2009, at 11:07 , Jonas Sicking wrote:
>>>>
>>>> '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.
>>>
>>> I take it in this case the Attr node is what's passed? Might give a
>>> reason
>>> for its sorry existence :)
>>
>> No, it'd be the element. The callback implementation would be
>> responsible for going through the attributes it is interested in and
>> take appropriate action based on their values.
>
> That's annoying. I'm guessing that creating an Attr node just for this
> purpose is overhead we can live without given that they're never used, but
> it would be cheap (and dead useful) to include at least the attribute's name
> (ns/ln pair).

Yeah, I definitely don't want to fore Attr nodes to be created just for this.

The reason for not supplying the ns/ln pair is that this way we can
fire the callback just once even if multiple attributes were changed
during a "DOM operation".

>>>> '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.
>>>
>>> If multiple nodes are changed at once, I'm guessing you call back with a
>>> DocumentFragment?
>>
>> No, you always call back with the node who had children added or
>> removed to it. So if I do
>>
>> myNode.innerHTML = "<i>hello</i>world<b>!</b>";
>>
>> Then there would be only a single call to each callback with myNode
>> passed as node. Even though 3 nodes are inserted, and all existing
>> nodes were removed.
>
> So if I'm myNode's great-great-great-grand-mother and am watching
> SubtreeChanged do I get called with myNode or myself?

It's called with myNode. I actually got this wrong in the algorithm in
my initial proposal. Here is a fixed version:

When one or more attributes change on N:

1. For each 'AttributeChanged' callback, C, registered on N, add an
item to the end of pendingCallbacks with the value <C, N>
2. Set P to N.
3. For each 'SubtreeAttributeChanged' callback, C, registered on P,
add an item to the end of pendingCallbacks with the value <C, N>
4. Set P to P's parentNode.
5. If P is non-null, go to step 3.
6. If notifyingCallbacks is set to true, or pendingCallbacks is empty,
abort these steps.
7. Set notifyingCallbacks to true.
8. Remove the first tuple from pendingCallbacks.
9. Call the handleEvent function on the callback in the tuple, with
the 'node' argument set to the node from the tuple.
10. If pendingCallbacks is not empty, go to step 8.
11. Set notifyingCallbacks to false.

(For childlist changes, run the above with 'ChildlistChanged' instead
of 'AttributeChanged', and 'SubtreeChanged' instead of
'SubtreeAttributeChanged')

>>> I'm thinking we might need ChildElementsChanged and
>>> ElementsSubtreeChanged
>>> that would handle only elements as well.
>>
>> When would these be called? Why isn't ChildlistChanged enough?
>
> I don't feel strongly about it — but it seems that every time we do
> something that's generic to nodes people come back and ask for an elements
> only variant, because that's what they actually use.

The only advantage I can see with this is that listeners that that
only care about the element tree, would avoid getting notified when
only non-element nodes were added or removed. The code in the listener
would be exactly the same. So it's strictly a performance
optimization.

On the flip side, for something like the innerHTML setter, the UA will
have to keep track of if the childlist used to contain any elements,
and then check if the new childlist contains any element. I'm not
convinced that this is worth it simply for a performance optimization.
At least not in the initial revision of this API.

>>> But then
>>> again, it seems to imply that I wouldn't get notified of changes to the
>>> PI's
>>> target.
>>
>> Ah, yeah, we could fire it for changes to the target too.
>
> Well, if you're firing on PIs it could be useful :)

Actually, target is readonly.

>>> If on the other hand I listen to this on an element, I probably don't
>>> want
>>> to hear about changes made to PIs (or comments) in the subtree.
>>
>> I think PIs and comments are changed rarely enough that this isn't
>> worth optimizing for. Unless you are worried about bugs somehow
>> arising?
>
> Precisely because they're rare, I'm worried about bugs when all of a sudden
> they're triggered. I think that there's a distinction (in authors' minds)
> between "content" — which includes text — and "the other stuff" — like PIs
> and comments. I think we should mirror that understanding, as it's rather
> sensible. It'd be a shame if 99.99% of code had to start with if
> (node.nodeType == PROCESSING_INSTRUCTION_NODE || node.nodeType ==
> COMMENT_NODE) return; and half of it forgot to.

Note that the mutation event DOMCharacterDataModified fires on comment
nodes too. Has it been a problem there?

The only way I can think of people using either mutation the mutation
event, or this new API, is that once you get notified, you find the
node you are interested in, and then get that nodes .textContent. This
will work fine even the node being changed was a comment or PI.

Do you have an example of a code pattern that would result in bugs for
unexpected comment/PI mutations?

/ Jonas

Received on Thursday, 4 June 2009 19:45:00 UTC