I want to loop through a nested HTML DOM node, as shown below:
<div id="main">
<div class="nested-div-one">
<div class="nested-div-two">
<div class="nested-div-three">
</div>
</div>
</div>
<div class="nested-div-one">
<div class="nested-div-two">
<div class="nested-div-three">
</div>
</div>
</div>
</div>
How would I do this using Javascript to loop through every single one of the dividers?
I am guessing OP was not specific for DIV elements, here's a more dynamic approach:
So first you wanna get the first container, in your case it's:
var mainEl = document.getElementById('main');
Once you have that, each DOM element has a .children property with all child nodes. Since DOM is a tree object, you can also add a flag to achieve recursive behavior.
function visitChildren(el, visitor, recursive) {
for(var i = 0; i < el.children.length; i++) {
visitor(children[i]);
if(recursive)
visitChildren(children[i], visitor, recursive);
}
}
And now, let's say you want to change all div backgrounds to red:
visitChildren(mainEl, function(el) { el.style.background = 'red' });
You can use vanilla javascript for this
document.querySelectorAll('div').forEach(el => {
// el = div element
console.log(el);
});
Related
I've got some html
<h4 id="start-here">title</h4>
<p>paragraph</p>
<p>paragraph</p>
...some number of paragraphs...
link
And I've got the <h4> with the id selected in JavaScript. How do I get from that selection in JS to the first <a> which is of the class link, or just the next sibling anchor tag?
Using document.querySelector() and a CSS selector, here with the general sibling combinator ~, you can achieve that like this:
A side note, in below samples I target inline style, though it is in general better to toggle a class.
Stack snippet
(function(){
document.querySelector('#start-here ~ a.link').style.color = 'red';
})();
<h4 id="start-here">title</h4>
<p>paragraph</p>
link
<p>paragraph</p>
link
Updated based on another question/comment, how to get more than one element in return.
With document.querySelectorAll() one can do similar, and target multiple elements like this.
Stack snippet
(function(){
var elements = document.querySelectorAll('#div2, #div3');
for (var i = 0; i < elements.length; i++) {
elements[i].style.color = 'red';
}
})();
<h4 id="start-here1">title</h4>
<div id="div1">some text</div>
<h4 id="start-here2">title</h4>
<div id="div2">some text</div>
<h4 id="start-here3">title</h4>
<div id="div3">some text</div>
The "start-here" ID on your element makes this easy. But let's imagine you have a reference to a DOM element without such a convenient selector, and you don't want to add a temporary ID to it.
In that case, you could use XPath with document.evaluate and your DOM reference as the second argument. Let's say you have that reference in yourElement and you want the next <section> sibling
const nextSibling = document.evaluate("following-sibling::section", yourElement, null,
XPathResult.FIRST_ORDERED_NODE_TYPE).singleNodeValue
I think to start with the first sibling, then i put all the siblings inside an array. Hence I extract what you want.
var x = document.getElementById("stat-here");
console.log(x)
var result = [],
node = x.nextSibling;
while ( node ) {
if (node.nodeType === Node.ELEMENT_NODE ) {
result.push( node );
}
node = node.nextElementSibling || node.nextSibling;
}
console.log(result, '\n Result: ',result[result.length-2])
<h4 id="stat-here">title</h4>
<p>paragraph</p>
<p>paragraph</p>
link
I have this HTML setup:
<div class="one">
<div class="text">One</div>
<div class="text">One</div>
</div>
<div class="two">
<div class="text">Two</div>
<div class="text">Two</div>
</div>
I want to move the content of div .two into .one using pure javascript (not jQuery) so we get:
<div class="one">
<div class="text">One</div>
<div class="text">One</div>
<div class="text">Two</div>
<div class="text">Two</div>
</div>
What is the best way to do this with millisecond performance in mind?
I personally prefer insertAdjacentElement as it gives you more control as to where to put elements, but be careful to take note of its browser support.
const one = document.querySelector('.one');
const two = document.querySelector('.two');
[...two.children].forEach(element => {
one.insertAdjacentElement('beforeEnd', element);
});
<div class="one">
<div class="text">One</div>
<div class="text">One</div>
</div>
<div class="two">
<div class="text">Two</div>
<div class="text">Two</div>
</div>
Also note that I've used ES2015 syntax.
The possible duplicate question actually has a native answer - use .appendChild() to move the nodes.
In your case, the code would look like this:
var one = document.querySelector(".one");
var children = document.querySelector(".two").children;
Array.prototype.forEach.call(children, function (child) {
one.appendChild(child);
});
You can loop over it with a while loop and use a DocumentFragment if you're after the performance boost.
var one = document.querySelector(".one");
var children = document.querySelector(".two").children;
var frag = document.createDocumentFragment();
while (children.length) {
frag.appendChild(children[0]);
}
one.appendChild(frag);
Faster solution (source):
var one = document.querySelector(".one");
var children = [...document.querySelector(".two").children];
var frag = document.createDocumentFragment();
var i = 0;
var il = children.length;
while (i < il) {
frag.appendChild(children[i]);
i += 1;
}
one.appendChild(frag);
Pure Javascript, compatible with most browser and old versions, use querySelector as its better in performance to get the first found class instead getElementsByClassName thats returns an array.
var one = document.querySelector('.one');
var two = document.querySelector('.two');
If you want to set it as last children, then:
while(two.children.length>0)
one.appendChild(two.children[0]);
If you want it as first children, then move the content before the first child of one:
while(two.children.length>0)
one.insertBefore(two.children[0], one.firstChild);
And last, useful for similar cases a clean replacement for one could be with:
one.innerHTML = two.innerHTML;
Note: this last implementation removes all content of "one" and sets the content by "two"
Here's the html:
<div class="col-sm-12" id="ProdutosPedido">
<div class="row">
<div class="col-sm-12 formProdutoAdd" id="produto_1">
...
</div>
</div>
</div>
As things happen within the page, divs are appended inside #ProdutosPedido, and #produto_1 increments to #produto_2 and so on.
This is not working for me:
console.log($("#ProdutosPedido > [id^=produto_]").length);
I need to iterate over these "produto_" and use the 'i' to refer to the current div, but I don't know how to do it. My example logs 0, and that should not be the case, since it starts with 1.
Since your produto divs are not direct children of ProdutosPedido, but its descendants, you need to use the following selector:
$("#ProdutosPedido [id^=produto_]")
Here is the working JSFiddle demo.
Pure Javascript Solution
function CountDiv() {
var nodes = document.getElementById('ProdutosPedido').getElementsByTagName('*');
var Count = 0;
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].id.substring(0, 8) == 'produto_')
Count++;
}
alert(Count);
}
I've been trying to write something to remove all nodes within an element, including the nodes within those nodes. I feel like I've come pretty close with this
function clear(node) {
var l = node.childNodes.length;
for (var i = 0; i < l; i++) {
console.log(i, node.childNodes);
var child = node.childNodes[i];
if (!child.hasChildNodes()) {
var newchild = child.parentNode;
newchild.removeChild(child);
console.log("removed" + child);
clear(newchild);
} else {
clear(child);
}
}
}
And the markup looks something like this
<div>
<canvas></canvas>
<div id="diagbox">
<div id="diag_text">Here's some text</div>
</div>
<ul id="option_ul">
<li>Some text</li>
<li>Some text</li>
</ul>
</div>
And this is how far it gets before failing:
<div>
<div id="diagbox">
<div id="diag_text"></div>
</div>
<ul id="option_ul">
</ul>
</div>
It removes the text node in diag_text, but fails to remove the actual div. It also removes the text nodes in the li elements, and for some reason succeeds to remove the li elements as well, but stops at the ul. The canvas is also succesfully removed, which is odd because it's top level and I was assuming that's why the others weren't working. The whole thing stops because at some point a TypeError occurs, "child is undefined".
Not quite sure where I'm going wrong here
EDIT: Definitely should have specified: I'm wanting to go through and remove all of the nodes and not just the parents because the content of these elements are actually dynamically generated and need to be scrubbed for different use cases.
I think the recursive solution may be a lot simpler than you had in mind.
This function will delete each node only after all of its child nodes have been deleted, so you can scrub/free any resources you need to reclaim.
Working Live Example (open console):
var n = document.getElementById("parent");
function clearInner(node) {
while (node.hasChildNodes()) {
clear(node.firstChild);
}
}
function clear(node) {
while (node.hasChildNodes()) {
clear(node.firstChild);
}
node.parentNode.removeChild(node);
console.log(node, "cleared!");
}
clearInner(n);
<div id="parent">
<div></div>
<div></div>
<div>
<div></div>
<div></div>
<div>
<div></div>
<div></div>
</div>
</div>
<div></div>
<div></div>
</div>
Why not just
var n = document.getElementById("parent");
n.innerHTML="";
Firstly you don't need to remove the child nodes. Removing the parent will do the trick. So, something of the form:
while (node.firstChild) {
node.removeChild(node.firstChild)
}
will do the trick.
As to your specific question: childNodes is dynamic. As your remove elements it changes, so indexing into it from a static length will not work. You will note that in my example above I only ever look at firstChild, since after each removeChild it will point to a new element.
So to fix your code either use something dynamic like this or process the array in reverse:
for (var i = l - 1; i >= 0; i--) {...
This will work because you are chopping nodes from the end and the previous children remain in place.
I have a parent div and it has 9 same div's am trying to swap two div's index. Following is my code:
HTML:
<div id="cont" class="container">
<div class="no">1</div>
<div class="no">2</div>
<div class="no">3</div>
<div class="blank"></div>
<div class="no">4</div>
<div class="no">5</div>
<div class="no">6</div>
<div class="no">7</div>
<div class="no">8</div>
</div>
now I want to swap say 5th and 6th indexed elements. I have no clue how to do that in JavaScript. I know there is function called .index() but how to do that in pure JS.
Here's one implementation: http://jsfiddle.net/x8hWj/2/
function swap(idx1, idx2) {
var container = document.getElementById('cont');
// ditch text nodes and the like
var children = Array.prototype.filter.call(
container.childNodes,
function(node) {
return node.nodeType === 1;
}
);
// get references to the relevant children
var el1 = children[idx1];
var el2 = children[idx2];
var el2next = children[idx2 + 1];
// put the second element before the first
container.insertBefore(el2, el1);
// now put the first element where the second used to be
if (el2next) container.insertBefore(el1, el2next);
else container.appendChild(el1);
}
This starts by getting a list of all element child nodes, then uses insertBefore to rearrange them.