How to iterate through a DOM tree? - javascript

I'm working on a website which uses a tree structure made the following way : a list is created and if an element of this list is itself a list then it is append to the first list using appendChild. The big list is put in a div named 'tree'.
I would like to access the content of the div 'tree' and go through the nodes to display them.
I've tried to code it several ways but it doesn't work, does someone has any idea how to do it ?
Edit (my code was too long)

This function iterates through a DOM tree. You can pass it a callback that takes the parent and child element too:
_SU3.treeIterate = function(parent, callback) {
var children = _SU3.getChildren(parent);
for (var i = 0; i < children.length; i++) {
var child = children[i];
callback(parent, child);
_SU3.treeIterate(child, callback);
}
};
_SU3.getChildren = function(parent) {
var children = new Array();
var childNodes = parent.childNodes;
if (childNodes == null)
return children;
for (var i = 0; i < childNodes.length; i++) {
var child = childNodes[i];
if (child.tagName == "li" || child.tagName == "LI") {
children.push(child);
}
}
return children;
};
Note: in this example the getChildren() function only finds "li" items. Amend as necessary.

If you think you will need several other functions on the tree, I suggest you to have a look on jsTree

Related

Javascript building element reference node list

How can i build a nodelist, which is reference to the selected node?
Example document.querySelectorAll('tr') returns a nodelist contains all the tr element node references on page.
I`m writing methods, to return element nodelist references by search criteria.
But if i using insertBefore, appendChild, etc... this function will remove the nodes from the elements.
Example i have a 4x4 table, and i want to return the first 2 rows:
var nl = document.createDocumentFragment();
el = document.querySelectorAll('tr');
for ( var a = 0; a < 2 && a < el.length; a++)
{
nl.appendChild(el[a]);
}
console.log(nl);
This nl returns the first 2 rows of the table, but it will removes from the document. Removed from the DOM and append to nl as child.
I want to retrieve only the node(s) reference like querySelectorAll as a list, not as documentFragment childNodes.
Any idea how to archieve this?
If you don't want to move things around in the DOM, don't use the methods that move things around!
var nl = new Array();
var el = document.querySelectorAll('tr');
for ( var a = 0; a < 2 && a < el.length; a++)
{
nl.push(el[a]);
}

Get all children of a node in javascript

I want to get all children of <p>.
HTML:
<p id="p">nodlist is an <i> ordered <b>collection of node</b> objects that are </i>children of the current element.<font color="blue"> If the element</font> has no children, then contains no node.</p>
JavaScript:
var childrens = [];
function getchilds(node){
if(node.nodeType == 1){
var childnodes = node.childNodes;
for(i=0; i<childnodes.length; i++){
var child = childnodes[i];
if(child.nodeType == 1)
getchilds(child);
}
}else
childrens.push(node);
}
var childnodes = document.getElementById('p').childNodes;
for(i=0; i<childnodes.length; i++){
getchilds(childnodes[i]);
}
but childrens[] is:
0 : nodlist is an
1 : ordered
2 : collection of node
3 : objects that are
4 : has no children, then contains no node.
children of the current element and If the element have missed, and when I add <a> element like link to p tag, the script gets stuck in an infinite loop.
Looks like you're using a global variable i for the loops which means you're going to miss elements when returning from an inner function call. Change it to
for(var i=0; i<childnodes.length; i++){
in both loops.

How to get child element in ExtJS 4

How to get all the child elements or id of a Panel in ExtJS 4?
I wrote that function for you. I think it will help you.
function getAllChildren (panel) {
/*Get children of passed panel or an empty array if it doesn't have thems.*/
var children = panel.items ? panel.items.items : [];
/*For each child get their children and concatenate to result.*/
Ext.each(children, function (child) {
children = children.concat(getAllChildren(child));
})
return children;
}
It takes panel (container) as a parameter and returns all children and subchildren recursively.
EDIT
This will return ids of children. USES PREVIOUS FUNCTION - getAllChilden
function getAllChildenIds(panel) {
//*Get all child items. \*/
var children = getAllChilden(panel);
//*Replace items with their ids.\*/
for (var i=0, l=children.length; i < l; i++) {
children[i] = children[i].getId();
}
return children;
}
Just call query() on your panel which will return an array of all child elements which match an optional selector.
i.e. panel.query()

trouble creating nested dom nodes in javascript

I've a function that takes an object as a parameter, and uses the structure of the object to create nested DOM nodes, but I receive the following error:
http://new.app/:75NOT_FOUND_ERR: DOM Exception 8: An attempt was made to reference a Node in a context where it does not exist.
What I would like my function to do, is, when supplied with a suitable object as a parameter, example:
var nodes = {
tweet: {
children: {
screen_name: {
tag: "h2"
},
text: {
tag: "p"
}
},
tag: "article"
}
};
It would create the following DOM nodes:
<article>
<h2></h2>
<p></p>
</article>
Here is my attempt so far:
function create(obj) {
for(i in obj){
var tmp = document.createElement(obj[i].tag);
if(obj[i].children) {
tmp.appendChild(create(obj[i].children)); /* error */
};
document.getElementById("tweets").appendChild(tmp);
};
};
I'm already struggling!
Ideally I'd like to eventually add more child key's to each object, not just tag, but also id, innerHTML, class etc.
Any hel would be much appreciated, though please: I'm sure a framework or library could do this for me in just a few lines of code, or something similar, but I'd prefer not to use one for this particular project.
If you could briefly explain your answers too it'd really help me learn how this all works, and where I went wrong!
Thank you!
NB: I've changed and marked the line in my function that the error message is talking about.
I changed it from:
mp.appendChild(obj[i].children);
to:
mp.appendChild(create(obj[i].children));
This is because I want any nested keys in the children object to also be created, so screen_name had a children key, they too would be created. Sorry, I hope you can understand this!
I'm looking at http://jsperf.com/create-nested-dom-structure for some pointers, this may help you too!
Your "create" function is going to have to be written recursively.
To create a node from your data (in general), you need to:
Find the "tag" property and create a new element
Give the element the "id" value of the element (taken from the data)
For each element in "children", make a node and append it
Thus:
function create(elementDescription) {
var nodes = [];
for (var n in elementDescription) {
if (!elementDescription.hasOwnProperty(n)) continue;
var elem = elementDescription[n];
var node = document.createElement(elem.tag);
node.id = n; // optional step
var cnodes = create(elem.children);
for (var c = 0; c < cnodes.length; ++c)
node.appendChild(cnodes[c]);
nodes.push(node);
}
return nodes;
}
That will return an array of document elements created from the original "specification" object. Thus from your example, you'd call:
var createdNodes = create(nodes);
and "createdNodes" would be an array of one element, an <article> tag with id "tweets". That element would have two children, an <h2> tag with id "screen_name" and a <p> tag with id "text". (Now that I think of it, you might want to skip the "id" assignment unless the node description has an explicit "id" entry, or something.)
Thus if you have a <div> in your page called "tweets" (to use your example, though if so you'd definitely want to cut out the "id" setting part of my function), you'd add the results like this:
var createdNodes = create(nodes), tweets = document.getElementById('tweets');
for (var eindex = 0; eindex < createdNodes.length; ++eindex)
tweets.appendChild(createdNodes[eindex]);
I added a function appendList that accepts a list of elements, and the container to append to. I removed the append to "tweets" part out of the create function to more effectively separate your code.
function create(obj) {
var els = [];
for(i in obj){
var tmp = document.createElement(obj[i].tag);
var children;
if(children = obj[i].children) {
var childEls = create(children);
appendList(childEls, tmp);
}
els.push(tmp);
};
return els;
};
function appendList(list, container){
for(var i = 0, el; el = list[i]; i++){
container.appendChild(el);
}
};
// gets an array of root elements populated with children
var els = create(nodes);
// appends the array to "tweets"
appendList(els, document.getElementById("tweets"));
Building on the previous answer:
I think you still need to create the element you're trying to append:
tmp.appendChild(children[prop].tag);
should be
tmp.appendChild(document.createElement(children[prop].tag));
function create(obj) {
for(i in obj){
var tmp = document.createElement(obj[i].tag);
var children;
if(children = obj[i].children) {
for(var prop in children)
tmp.appendChild(document.createElement(children[prop].tag));
}
document.getElementById("tweets").appendChild(tmp);
};
};

Remove All Nodes with (nodeName = "script") from a Document Fragment *before placing it in dom*

My goal is to remove all <[script]> nodes from a document fragment (leaving the rest of the fragment intact) before inserting the fragment into the dom.
My fragment is created by and looks something like this:
range = document.createRange();
range.selectNode(document.getElementsByTagName("body").item(0));
documentFragment = range.cloneContents();
sasDom.insertBefore(documentFragment, credit);
document.body.appendChild(documentFragment);
I got good range walker suggestions in a separate post, but realized I asked the wrong question. I got an answer about ranges, but what I meant to ask about was a document fragment (or perhaps there's a way to set a range of the fragment? hrmmm). The walker provided was:
function actOnElementsInRange(range, func) {
function isContainedInRange(el, range) {
var elRange = range.cloneRange();
elRange.selectNode(el);
return range.compareBoundaryPoints(Range.START_TO_START, elRange) <= 0
&& range.compareBoundaryPoints(Range.END_TO_END, elRange) >= 0;
}
var rangeStartElement = range.startContainer;
if (rangeStartElement.nodeType == 3) {
rangeStartElement = rangeStartElement.parentNode;
}
var rangeEndElement = range.endContainer;
if (rangeEndElement.nodeType == 3) {
rangeEndElement = rangeEndElement.parentNode;
}
var isInRange = function(el) {
return (el === rangeStartElement || el === rangeEndElement ||
isContainedInRange(el, range))
? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
};
var container = range.commonAncestorContainer;
if (container.nodeType != 1) {
container = container.parentNode;
}
var walker = document.createTreeWalker(document,
NodeFilter.SHOW_ELEMENT, isInRange, false);
while (walker.nextNode()) {
func(walker.currentNode);
}
}
actOnElementsInRange(range, function(el) {
el.removeAttribute("id");
});
That walker code is lifted from: Remove All id Attributes from nodes in a Range of Fragment
PLEASE No libraries (ie jQuery). I want to do this the raw way. Thanks in advance for your help
The easiest way to gather all <script> nodes would be to use getElementsByTagName, but unfortunately that is not implemented on DocumentFragment.
However, you could create a temporary container and append all elements within the fragment, and then go through and remove all <script> elements, like so:
var temp = document.createElement('div');
while (documentFragment.firstChild)
temp.appendChild(documentFragment.firstChild);
var scripts = temp.getElementsByTagName('script');
var length = scripts.length;
while (length--)
scripts[length].parentNode.removeChild(scripts[length]);
// Add elements back to fragment:
while (temp.firstChild)
documentFragment.appendChild(temp.firstChild);
Correct me if I'm wrong, but if the documentFragment is a real DOM Fragment, you should be able to do something like:
var scripts = documentFragment.getElementsByTagName('script');
if (scripts.length){
for (var i=0, l = scripts.length;i<l;i++){
documentFragment.removeChild(scripts[i]);
}
}
right?
Correction: you can't apply getElementsByTagName to a documentFragment, J-P is right. You can however us a child of the fragment if it is a (cloned) node supporting getElementsByTagName. Here's some (working) code I use within a larger script a few days ago:
var fragment = d.createDocumentFragment(), f;
fragment.appendChild(document.createElement('div'));
fragment.firstChild.appendChild(zoeklijst.cloneNode(true));
f = fragment.firstChild;
return f.getElementsByTagName(getList); //<==
2022, Chrome, querySelector family works on document fragments:
frag.content.querySelectorAll('script').forEach(
(s)=>s.remove()
);

Categories