- From: Aryeh Gregor <Simetrical+w3c@gmail.com>
- Date: Tue, 29 Mar 2011 14:19:34 -0400
On Mon, Mar 28, 2011 at 8:05 PM, Boris Zbarsky <bzbarsky at mit.edu> wrote: > One problem here is that there is not a concept of "moved" in the DOM. > There's just removed and inserted. ?Trying to insert something that already > has a parent will remove it and then do the insert. ?Thanks to the wonders > of mutation events and userdata, script can execute in state when the node > being moved is not in the DOM, which means that ranges need to update > separately for the removal and insertion because the state of the range in > the intermediate state with the node outside the DOM needs to be > well-defined. Hmm, I see. I was assuming that insertBefore() would just atomically change the parent from the new one to the old one. Looking more closely at DOM Core, I see it's specced the way you describe it (although nothing says when the mutation events fire). That's certainly a problem for my plan. It would be possible to work around it by requiring that insertBefore() and similar methods do special magic for Ranges independent of the actual DOM mutations done, however. That already has to happen for insertData()/deleteData()/appendData(), right? All browsers treat those differently from just setting the data to the equivalent string. > Now if we dropped support for mutation events and userdata handlers > first..... Is that feasible? I get the impression implementers would all love it, but somehow they haven't done it yet. > Gecko's implementation of A.insertBefore(B, C), in pseudocode and ignoring > all the sanity-checking and for cases when B is not a document fragment is: > > ?if (B.ownerDocument != A.ownerDocument) { > ? ?A.ownerDocument.adoptNode(B); ?// This can run arbitrary script > ?} > > ?if (B.parentNode) { > ? ?B.parentNode.removeChild(B); ? // This can run arbitrary script > ?} > > ?// Mutate the child storage of A to put B in the child list; notify > ?// observers > > Both the removeChild call and the mutation of A's child list notify mutation > observers. ?Ranges are mutation observers, and use the notifications to > update themselves. ?So from the point of view of a range, the removal and > insertion are two distinct events. > > -Boris > > P.S. ?I suspect that actual interoperability for the edge cases of this > stuff is poor: nothing defines whether the adopt happens before the > removeChild above, nothing defines what happens if user data handlers or > mutation events mutate the DOM, etc. DOM Core says it's supposed to be basically if (B.ownerDocument != A.ownerDocument) { A.ownerDocument.adoptNode(B); } // Insert into the children <http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-insertbefore> where adoptNode() removes the node from its parent before changing the ownerDocument. This seems to be how both IE, Gecko and Opera actually behave -- moving a node from one document to another has the ownerDocument equal to the old document on DOMNodeRemoved, and the new document on DOMNodeInserted. (WebKit throws if you do insertBefore() cross-document, and I can't get it to fire any DOM mutation events even if it's same-document.) When I thoughtlessly mutated the DOM from the event handler, though, I definitely hit a lack of interop. Anyway, then what does Gecko do for execCommand()? Does that just have special-case logic to adjust the selection to fit the new DOM? It would be nice if we could eliminate that in a clean way, from both the specs and implementations.
Received on Tuesday, 29 March 2011 11:19:34 UTC