HTML
<div id="divID">
<label></label>
<textarea></textarea>
</div>
JavaScript
function invisible(){
let div = document.getElementById("divID");
let children = div.childNodes;
for (let i = 0; i < children.length; i++) {
children[i].style.display = "none";
}
}
After calling the function nothing happens. What I'm doing wrong?
You have to set divID to your div tag.
<div id="divID">
<label></label>
<textarea></textarea>
</div>
And then you have to use div.children in invisible function.
function invisible(){
let div = document.getElementById("divID");
let children = div.children;
for (let i = 0; i < children.length; i++) {
children[i].style.display = "none";
}
}
<input type="button" onClick=" invisible()" value="Remove" />
<div id="divID">
<label></label>
<textarea></textarea>
</div>
You can make the function more reusable by accepting the element whose children are to be hidden as the first argument. It currently only works for the element with id of "divID".
function invisible(parent){
for(const child of parent.children) child.style.display = 'none';
}
invisible(document.querySelector('div'))
<div>
<label>Label</label>
<textarea>Textarea</textarea>
</div>
The problem seems to be your use of childNodes instead of children as other answers have pointed out.
This answer attempts to give some more context on what is happening in addition to the other answers.
let div = document.getElementById("divID");
console.log(div.childNodes)
// Returns something like
NodeList(5) [text, label, text, textarea, text]
console.log(div.children)
// Returns something like
HTMLCollection(2) [label, textarea]
So childNodes returns a NodeList and children returns an HTMLCollection.
The definition children on mdn explains the difference between children and childNodes:
Element.children includes only element nodes. To get all child nodes, including non-element nodes like text and comment nodes, use Node.childNodes.
The problem is that these text nodes don't have a style property. So it returns undefined. Trying to access the display property on style then causes a TypeError. Since the first element is a text element the loop fails immediately and the label and textarea are never hidden.
use children instead of childNodes:
function invisible(){
let div = document.getElementById("divID");
let children = div.children; //<== children instead of childNodes
for (let i = 0; i < children.length; i++) {
children[i].style.display = "none";
}
}
//just to test after 1.5 sec all children of divID will be removed(display=none)
setTimeout(invisible, 1500)
<div id='divID'>
<label>test1</label>
<textarea>test2</textarea>
</div>
Related
Can somebody explain me what did I do wrong?
let toolbarForChilds = document.getElementById("toolbar");
for (let i = 0; i < toolbarForChilds.childNodes.length; i++) {
toolbarForChilds.childNodes[i].style.cursor = "default";
}
<div class="sample-toolbar" id="toolbar" style="text-align: center;">
<div id="title">#1</div>
<div id="another thing">#2</div>
</div>
I get this error in the console:
Uncaught TypeError: Cannot set property 'cursor' of undefined
Node.childNodes
childNodes includes all child nodes—including non-element nodes like text and comment nodes. To get a collection of only elements, use ParentNode.children instead.
You do not need to use childNodes. You can target all the elements with querySelectorAll() and loop through them like the following way:
let toolbarForChilds = document.querySelectorAll("#toolbar div");
for (let i = 0; i < toolbarForChilds.length; i++) {
toolbarForChilds[i].style.cursor = "default";
}
<div class="sample-toolbar" id="toolbar" style ="text-align: center;">
<div id ="title">#1</div>
<div id ="another thing">#2</div>
</div>
The problem here is that childNodes not only returns elements. There are also nodes without a style attribute. As example between the html tags there are empty text nodes. You should see this in your browsers console if you add console.log(toolbarForChilds.childNodes[i]); inside the for loop.
The reason your code doesn't work is that childNodes also includes the newlines between the elements. You can console.log the childNodes to see them. Your code will work if you wrap it in a try-catch to skip those, although you should follow Mamun's answer for a cleaner way to do it.
for (let i = 0; i < toolbarForChilds.childNodes.length; i++) {
try {
toolbarForChilds.childNodes[i].style.cursor = "default";
}
catch {
continue;
}
}
node.childNodes returns elements without style attributes like text nodes. For only getting node elements, that is, div's: id ="title" & id ="another thing" use node.children. This is implemented below.
let toolbarForChilds= document.getElementById("toolbar");
/* console.log(toolbarForChilds.childNodes); */
for (let i = 0; i < toolbarForChilds.children.length; i++) {
toolbarForChilds.children[i].style.cursor = "help";
}
<div class="sample-toolbar" id="toolbar" style ="text-align: center;">
<div id ="title">#1</div>
<div id ="another thing">#2</div>
</div>
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);
});
I'm trying to get the attributes of an element's children ... something like this:
<div id="mydivs">
<div name="mydiv1" name="format1">
</div>
<div name="mydiv2" format="format2">
</div>
</div>
var parent = document.getElementById('mydivs');
var children = parent.childNodes;
for (let child of children) {
console.log(child)
console.log(child.attributes)
}
child.atrribute returns undefined
JsFiddle: https://jsfiddle.net/8tdac5fn/
Edit: Another option would be to use children instead of childNodes
var parent = document.getElementById('mydivs');
var children = parent.children;
parent.childNodes will include text nodes and that is why you are getting undefined, so you can first check if nodeType is 1 which is Node.ELEMENT_NODE.
Then you could use spread syntax in array to create array of attribute nodes so you can use forEach loop.
var parent = document.getElementById('mydivs');
var children = parent.childNodes;
for (let child of children) {
if (child.nodeType === 1) {
[...child.attributes].forEach(({name,value}) => {
console.log(`${name} - ${value}`);
})
}
}
<div id="mydivs">
<div name="mydiv1" name="format1"></div>
<div name="mydiv2" format="format2"></div>
</div>
I need to get user entered markup (tables) and if the table does not contain a div with a the class="table", I need to add the div & class. I need to ignore any other children, such as p, span, and only target elements with tables.
<div class="parent">
<div class="table"><table></table></div>
<div class="table"><table></table></div>
<table></table>
<div><table></table></div>
<div class="table"><table></table></div>
<p></p>
<span></span>
</div>
You can see in the nodelist above, node index 2,3 both need a wrapper div with the class="table", but ignore the p and span.
[].map.call(table, (node) => {
if (!node.parentNode.classList.contains('table')) {
const parent = document.getElementsByClassName('parent');
[].map.call(parent, (nodeChild) => {
const addWrap = document.createElement('div');
addWrap.classList.add('table');
addWrap.appendChild(node);
nodeChild.append(addWrap);
});
}
});
I have tried this, but it appends the node with a wrapper div at the bottom of the index. How do I get the nodes to append in their correct order with the wrapper div? Thanks.
You're better off using a for/loop (using map here is an anti-pattern) to iterate over the child nodes of the parent node and replace the current child node with the new created element.
childNodes
replaceChild
Inspect the output in dev tools to see the change.
const parent = document.querySelector('.parent');
const { childNodes } = parent;
for (let i = 0; i < childNodes.length; i++) {
const el = childNodes[i];
// If the node type is an element
if (el.nodeType === 1) {
// If element is a div add a class
// if it's missing
if (el.tagName === 'DIV' && !el.classList.contains('table')) {
el.classList.add('table');
}
if (el.tagName === 'TABLE') {
const div = document.createElement('div');
div.classList.add('table');
div.appendChild(el);
// Replace the child node with the new div node
parent.replaceChild(div, childNodes[i]);
}
}
};
<div class="parent">
<div class="table"><table></table></div>
<div class="table"><table></table></div>
<table></table>
<div><table></table></div>
<div class="table"><table></table></div>
<p></p>
<span></span>
</div>
I think I understood what you're trying to do. This should find child nodes without a "table" class and wrap them in a div with a "table" class. When you run the snippet it won't show any thing as your elements don't have any content but you should be able to inspect them to see the changes.
// get all parent divs
var parents = document.getElementsByClassName("parent");
// loop over parent divs
for (var i = 0; i < parents.length; i++) {
// get a single parent div
var parent = parents[i];
// loop over child nodes
for (var j = 0; j < parent.childNodes.length; j++) {
// get a single child node
var childNode = parent.childNodes[j];
// if this child node is type 1 (element node)
// and does not have a class of table
if (
childNode.nodeType === 1 &&
!childNode.classList.contains("table")
) {
// create a new div element
var wrap = document.createElement('div');
// give it a class of table
wrap.classList.add("table");
// append a clone of the child node
wrap.appendChild(childNode.cloneNode());
// replace the old child node with the wrapped child node
parent.replaceChild(wrap, childNode);
}
}
}
<div class="parent">
<div class="table"></div>
<div class="table"></div>
<table></table>
<div></div>
<div class="table"></div>
</div>
I'm making an inventory page for a website. I want to be able to sort the items with check boxes. What i think i need is to apply a div class to each of them and use document.getElementsByClassName to change the display to none for the ones i want hidden.
Anyway i tried with document.getElementsById, but i could only change the first one with the corresponding Id. Apperarently i need to change the class. This is where im at now.
P.S The [sc:1 ] is shortcoder for wordpress. It seemed to work when i used Id.
<script>
function toggle() {
var e = document.getElementsByClassName('truck');
if(e.style.display == "block")
e.style.display = "none";
else
e.style.display = 'block';
}
</script>
<input type="checkbox" id="checkme" onclick="toggle()">Dump Truck
<div class="truck">This is text that should hide</div>
<div class="truck">[sc:1 ]</div>
<div class="sweeper">[sc:2 ]</div>
<div class="sweeper">[sc:3 ]</div>
You have to iterate over the set that document.getElementsByClassName('truck'); returns:
for (var i = 0; i < e.length; i++) {
if(e[i].style.display == "block")
e[i].style.display = "none";
else
e[i].style.display = 'block';
}
document.getElementsByClassName returns a set of elements that match that class name, so you have an array of DOM elements: [elem1, elem2, elem3, ..]. You can't just simply set [].display == prop - you have to iterate over the set and set the display for each element.
document.getElementsByClassName('truck'); always returns the array list of all the elements that has the class "track", so even if you have a single element that has the class "truck" the getElementsByClassName('truck') would return the single element in an array.
Please note, get**Elements**ByClassName has the plural form of element which refers to a set of element items to be fetched.
For your case, you have to iterate the array items to get each element and perform operations.
Try the following code block instead
<script>
function toggle() {
var e = document.getElementsByClassName('truck');
// Get all the elements that has the class name 'truck', so for your example,
// there are 2 items and the array e would have 2 items (i.e. e[0] will have the
// div element that has 'This is text that should hide' as the inner HTML and e[1]
// will have the div element that has [sc:1 ] as the inner HTML
for (var x=0; x < e.length; x++) {
if(e[x].style.display == "block") // Get the xth element's style
e[x].style.display = "none";
else
e[x].style.display = 'block';
}
}
</script>
Please note, even if you have a single item with the class name "truck" you would find it on the 0th location of the returned array (i.e. e[0]).