Why does "appendChild" moves a node? - javascript

I'm playing around with native javascript. I'm basically practicing basic node manipulations such -- add, remove, move, copy, and create.
While testing move, I got a question.
http://jsfiddle.net/sJg7E/
if you look at the jsfiddle above, I've used "appendChild". How come it moves a node to a new div? I know that I need to clone a node if I want to copy a node. It just doesn't look/sound right with "appendChild" command.
Is this expected behavior?

A node can only have one parent. Therefore it moves it if you appends it to another node.
From documentation of appendChild:
Adds a node to the end of the list of children of a specified parent
node. If the node already exists it is removed from current parent
node, then added to new parent node.
From the same page:
You can use cloneNode to make a copy of the node before appending it
under the new parent. (Note that the copies made with cloneNode will
not be automatically kept in sync.)
Also note:
This method is not allowed to move nodes between different documents.
If you want to append node from a different document (for example to
display results from AJAX request) you must first use importNode.
You can also read the w3c specification of appendChild:
Adds the node newChild to the end of the list of children of this
node. If the newChild is already in the tree, it is first removed.

Related

What Does Cody Lindley Mean by "nodes contained in the collection are either literally part of the live document or a snapshot of the live document"?

I'm not getting this quote:
"A collection [of DOM Nodes] can either be live or static. Meaning that the nodes contained in the collection are either literally part of the live document or a snapshot of the live document."
I'm specifically confused about what he means by being "part of the live document or a snapshot of the live document". He doesn't expand on this.
My First Reaction
Is he referring to the ability of a collection to be holding either (1) nodes that still exists in the DOM (i.e. "live") or (2) nodes that no longer exist in the current DOM, such as when you may use a method that removes a node but also returns the node it removed (i.e. a snapshot)?
He never really expands on this so I'm wondering if I missed something earlier in the book, or am just lacking some prerequisite knowledge he assumes the reader should have.
Any help grokking this would be greatly appreciated! thanks.
The full excerpt is below:
1.13 Grokking node collections (i.e. Nodelist & HTMLcollection)
When selecting groups of nodes from a tree (cover in chaper 3) or
accessing pre-defined sets of nodes, the nodes are either placed in a
NodeList (e.g. document.querySelectorAll('*')) or HTMLCollection (e.g.
document.scripts). These array like (i.e. not a real Array) object
collections that have the following characteristics.
A collection can either be live or static. Meaning that the nodes
contained in the collection are either literally part of the live
document or a snapshot of the live document.
By default nodes are
sorted inside of the collection by tree order. Meaning the order
matches the liner path from tree trunk to branches.
The collections have a length property that reflects the number of elements in the list
From Cody Lindley's Dom Enlightenment:
http://domenlightenment.com/
if it helps, further reading:
Nodelist: https://www.w3.org/TR/dom/#nodelist
HTMLcollection: https://www.w3.org/TR/dom/#htmlcollection
Based on the information I've found - and posted in the comments - I summarize as:
When you query the DOM to retrieve a collection of nodes, depending on the method used (IE: Document.querySelectorAll(), Document.querySelect(), etc) you'll get a list of either live or static nodes, the difference being that changes made to the DOM will be reflected in the nodes already present in a live collection, whereas the same changes won't affect these same nodes if they're in a static collection.
So, if you want a snapshot in that moment in time where you queried the DOM, you should use methods that return a static collection. This way if the DOM is changed in any way whilst you're working with this collection, nothing will be applied to these elements in your collection. The opposite for the live collection.

How to append a node with children to an existing treeview with javascript

I am using Kendo UI 2012.2.607.
I am trying to append a node that I retrieved from an ajax call to add to my kendo tree view. However, despite the node matching the same model schema, only the top node is appended to the tree.
Example Node:
id: "333-333-333"
text: "Node1"
expanded: true
hasChildren: true
children: <Node2>
Node2 is of identical format. However, when I attempt to call TreeView.append("Node1") only the top level node (Node1) is appended, but not Node2, despite the tree showing Node1 should have children. Please note that the tree has loadOnDemand=true.
Is append not supposed to add the associated children?
It might help if I explain what I am trying to do. I have a potentially large tree and therefore use onDemand loading but also server side aids to limit the number of nodes per request. I provide a special node that when clicked, makes an ajax request to get more nodes and uses insertBefore to add some more.
I now have a search method as well, which i use to get the exact path to a specific node. What I want to use is this path in order to expand and add nodes dynamically (since the current node might not be loaded into the tree).

How can a DIV node been "detached" and what's the use?

In this article a detached Div node is created:
http://www.bennadel.com/blog/1008-jQuery-empty-Kills-Event-Binding-On-Persistent-Nodes.htm
I don't understand : I thought that DOM owns every nodes. How would you attach to DOM then ?
Last but not least what would be the purpose of having detached node ?
I'm not sure which answer you expect, so here are some thoughts:
I thought that DOM owns every nodes.
The document owns every node. Each node has an ownerDocument [MDN] property.
From the specification:
The Document interface represents the entire HTML or XML document. Conceptually, it is the root of the document tree, and provides the primary access to the document's data.
Since elements, text nodes, comments, processing instructions, etc. cannot exist outside the context of a Document, the Document interface also contains the factory methods needed to create these objects. The Node objects created have a ownerDocument attribute which associates them with the Document within whose context they were created.
How would you attach to DOM then?
There are various ways to insert a new node, such as appendChild [docs] or insertBefore [docs].
Last but not least what would be the purpose of having detached node ?
One advantage is that you can build complex subtrees offline so that the browser does not have to recalculate the layout every time you insert a node.
Sometimes it is also useful for parsing an HTML string. By creating an empty, detached div and assign the HTML string to innerHTML, you can parse and process the HTML string easily.
The only caveat is that document.getElementById cannot find nodes which are not part of the tree.
Also interesting in this regard might be the explanation for the Node.parentNode property. After all, a Node which does not have parent is not part of the tree:
The parent of this node. All nodes, except Attr, Document, DocumentFragment, Entity, and Notation may have a parent. However, if a node has just been created and not yet added to the tree, or if it has been removed from the tree, this is null.
with document.createElement(), you can create an element node
var p = document.createElement("p");
At this point though, it will exist in memory but will not have been attached to the DOM.
There are numerous ways in which a node can be attached to the DOM, but probably the easiest would be using element.appendChild(node)
var p = document.createElement("p");
// attach the newly created node to the document body
document.body.appendChild(p);
You may want to create the element first, manipulate it and then attach to the DOM so that your manipulations do not cause browser reflow e.g. if you're setting the background colour, border, appending child elements, etc. you want to do this in memory without each change having to be reflected as a visual change in the browser.
Modifying the DOM is expensive. You can create a detached object, set it up the way you need to (adding its attributes, binding event handlers, etc). After its setup the way you want, then append it to the DOM.
You can definitely create elements that are not part of the DOM.
var someElement = document.createElement("div");
var someOtherElement = $("<div>");
Performing operations on detached elements are far more efficient than performing operations on attached elements because detached elements do not have to be rendered.
The page you linked to uses .empty() which detaches elements from the DOM and removes all event handlers from those elements.
There is also .detach(), which may be what you mean. .detach() basically just, well, detaches the node from the DOM. It's not part of the DOM anymore; it's just hanging around in memory (as long as you keep it in a variable). This means that if you discard the variable, you've lost the detached node forever (it's not in the DOM nor in a variable).
A use case is e.g. temporarily "removing" an element from the DOM without actually dismissing it, so that you can attach it later (using .append/.after/etc).
var detached = $("...").detach();
// later:
$("body").append(detached);

How can I find a text node in the live DOM using its copy/clone?

I've copied some text nodes from the page (i.e., live DOM) and have stored them in an array. Is it possible to locate those exact nodes again in the live DOM? I would need to use plain JavaScript.
This is basically how my project works:
A configuration object specifies sections of the page (using CSS selectors) where there is some text content, and optionally subsections within that section (also using CSS selectors) where there is text content that should be ignored. The property names that I use, respectively, are select and exclude.
Using Sizzle (jQuery's selector engine), I generate an element collection for each of the select selectors, then clone it.
I then run the exclude selectors against the select collection, finding any elements that match, and remove them from the select collection.
Using the select collection with the removed exclude sections, I traverse it to build an array of only the text nodes.
I use this array of text nodes to do some word matching based on a supplied list of terms that may be in the original text content. To do this, I build an array of objects that include properties like the text node in which a term was found, and at what position/offset that term occurs within the text node's data.
Given the latter array, I need to be able to match the cloned text nodes in which matching terms were found to the original text nodes from which they were cloned. If I can do this, I can just iterate over my array of objects, first finding the text node that corresponds to the live DOM (from which it was originally cloned), and then linking the term at the position/offset that I have recorded in the object.
Hopefully this makes some sense - please let me know if not, and I can provide more details. This must be deterministic - i.e., I can't just search the live DOM for a text node that has the same data, as that may lead to false positives.
Again, I must use plain JavaScript here, not jQuery or any other libraries.
I appreciate any help!
In general, you can't. Once you've cloned, the new clones have a different identity to the old nodes. There is no general-purpose way to tie a node back to the node it was cloned from.
If you know the topmost cloned ancestor node, and the node in the document that it was cloned from, you can naturally use the index of each ancestor in its parent childNode list to walk down from the topmost ancestors to a particular node... but only assuming neither DOM has been mutated since cloning.
Otherwise, you'd be left with some horrid hack like walking the entire DOM of the clone immediately after cloning, writing an expando property to each node to refer to the source node.
I'm not sure why you are cloning nodes at all. In the process you describe, surely you could just store the live document nodes in your lists, without any cloning?

How to trigger rendering of JQuery treeview

I use jquery tree plugin to render hierarchical data.
I have coded additional functions which would allow user to interact with this data (like adding/deleting nodes, swapping nodes, etc...)
Currently this plugin supports that whenever you want to add any node, you can call following method,
$("#browser").treeview({
add: branches
});
here branches is jQuery object created with the HTML block, which would represent a particular node.
However, for delete and swapping of nodes, I use common JQuery functions like,
for delete,
$("#topnd2").remove();
for swapping,
var next = $("#topnd2").next();
$("#topnd2").insertAfter(next);
topnd2 is an id of any particular tree node.
The nodes get deleted / swapped properly but the problem is the tree does not get rendered and therefore the tree images (mainly vertical lines denoting branches) are not set properly.
For example, if I delete the last node then that node will be removed from rendered treeview but the remaining sibling node should get L as branch line image but not | .
I tried calling
$("#browser").treeview();
Please let me know your ideas.
Thanks,
Jatan
I found some workaround as given below,
Once the node is swapped up, virtually add its previous node to its child,
$("#browser").treeview({add:$("#topnd2").insertBefore(previous).next()});
If node is swapped down, virtuall add the current node to its next node.
$("#browser").treeview({add:$("#topnd2").insertAfter(next)});
currently it's working fine, will update this post, if I find any problems in this approach. Also please validate this approach if you know.
Regards,
Jatan
If you try to refresh the treeview again after node removal, the link will work but not the [+] or [-] icon. Tried this on several browsers..

Categories