How get ChildNodes > ChildNodes (Children of Children ) - javascript

Only JS no Jquery.
How to get All ChildNodes of container and than for each of the children get there children?
I don't want to add ID to each Wrapper that is child of grid-row, I am trying to target with "this" or at this index. This script should be dynamic without specifying any ID.
I can't use classes to get all Wrappers as I need to trigger each wrapper separately and apply changes to it.
I want to get all grid-row children "Wrapper" widths and store in a array.
I am using ChildNodes as it is compatible with all browsers.
var container = document.getElementById('container');
var rows = container.childNodes;
var rowslenght = container.childNodes.length;
var rowsArray = new Array();
for (var i=0; i < rowslenght ; i++) {
if (gridrow[i].nodeType == 1){ // this is to no retrieve text
// I got all the children of grid-row. How I get grid-row children.
// var rowsChildren = rows[i].getAttribute('id');
// here goes other if to go through each "Wrapper" width and set width
// console.log( rowsChildren);
console.log( rows);
return rowsArray;
}
}
<div id="container">
<div class="grid-row">
<div class="Wrapper">
<div class="block"></div>
</div>
<div class="Wrapper">
<div class="block"></div>
</div>
</div>
<div class="grid-row">
<div class="Wrapper">
<div class="block"></div>
</div>
<div class="Wrapper">
<div class="block"></div>
</div>
</div>
</div>

Try this.
var container = document.getElementById('container');
var rows = container.childNodes;
rows = removeTextNode(rows); // remove Text Nodes;
// Loop through .grid-row
forEach(rows, function(row){
// Get wrappers and filter them
var rowWrappers = row.childNodes;
rowWrappers = removeTextNode(rowWrappers);
// Now loop over the wrapper, and modify
// the current function adds `Wrapper-blue` to the wrappers.
forEach(rowWrappers, function(wrapper){
console.log(wrapper);
wrapper.classList += ' Wrapper-blue';
});
});
// this helper function removes extra spaces/breaklines which are considered as Nodes
function removeTextNode(nodes){
return [].filter.call(nodes, function(o){
return o.nodeType == Node.ELEMENT_NODE;
});
}
// Source: https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches/
function forEach(array, callback, scope) {
for (var i = 0; i < array.length; i++) {
// the first argument is thisArg which is the context and can used as `this` in the callback
callback.call(scope, array[i], i); // passes back stuff we need
}
};
.Wrapper-blue {
background:#ddd;
margin:5px;
width:60px;
height:60px;
}
<div id="container">
<div class="grid-row">
<div class="Wrapper">
<div class="block"></div>
</div>
<div class="Wrapper">
<div class="block"></div>
</div>
</div>
<div class="grid-row">
<div class="Wrapper">
<div class="block"></div>
</div>
<div class="Wrapper">
<div class="block"></div>
</div>
</div>
</div>

rows[i].childNodes will give you the Wrapper elements inside each grid-row, but then you will have to loop through them, too.

I was not able to get the second loop right as in the first loop I got all the childNodes including the empty text fields. I needed first to run the the for statement and than only filter the ones that has nodeType == 1.
var container = document.getElementById('container');
var rows = container.childNodes;
var rowslenght = container.childNodes.length;
var rowsArray = new Array();
// Get all the grid-row and run through them
for (var a=0; a < rowslenght ; a++) {
// If it is a HTML element than go through
if (gridrow[a].nodeType == 1){
var wpChildren = gridrow[a].childNodes;
var wpChildrenleght = gridrow[a].childNodes.length;
// Run through all the wrappers
for (var b =0; b < wpChildrenleght; b++){
// only get grid-wrapper html
if (wpChildren[b].nodeType == 1){
console.log(wpChildren[b]) // here is your specific div
}
}
}
}

This isn't as hard as you're making it; you don't have to do multiple loops.
The below is supported by all modern browsers, including IE 9+ https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
var container = document.getElementById('container');
var rows = container.getElementsByClassName('grid-row');
// NOTE: this assumes that you only want to get wrappers which are children of grid-rows which are children of a container
var wrapperChildren = [];
rows.forEach(function(row) {
var wrappers = row.getElementsByClassName('Wrapper');
// per comments, adding child count
row['data-child-count'] = wrappers.length;
wrapperChildren.concat(wrappers);
});
It gets easier still if the wrappers are only inside grid-rows already (then you could just select all of them by class name). Working fiddle
EDIT
Per the comment, you want to know how many children each row has, that's easy to add as a data attribute (or any other custom attribute) and then access it after this function has run.

Related

How do I properly add an element around each child element (before and after)

The title might be a little confusing so I will show an example of what I mean. Say I have some html code like this
<div id="parentElement">
<div class="A"></div>
<div class="B"></div>
<div class="C"></div>
</div>
I would then like to append a new element around each element, so it will turn into this
<div id="parentElement">
<div class="newElem"></div>
<div class="A"></div>
<div class="newElem"></div>
<div class="B"></div>
<div class="newElem"></div>
<div class="C"></div>
<div class="newElem"></div>
</div>
Two things to note is that first, there could be multiple children elements like A, B, C, D, E, ... And two, parentElement could have no children in it like
<div id="parentElement">
</div>
and if that's the case, one newElem will simply be added.
<div id="parentElement">
<div class="newElem"></div>
</div>
I can do this through a ton of code and a ton of checks and using jQuery's append and before and after and other methods, but I'm wondering if there's a more efficient/clean way of writing it out.
Thanks!
You really don't need any conditionals.
If you use children() and loop over them nothing will happen if there are none. If there are - you can use after(function) to do the looping and then finally use prepend() for the parent container whether there are children or not
var newElement = '<div class="newElement">New</div>';
$('#parentElement').children().after(function(){
return newElement;
}).end().prepend(newElement);
.newElement{ background:#ccc}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="parentElement">
<div class="A">A</div>
<div class="B">B</div>
<div class="C">C</div>
</div>
without jquery:
function init() {
var parent = document.getElementById('parentelement');
for (var i = 0; i < parent.childElementCount; i = i + 3) {
var newElement = document.createElement('div');
newElement.appendChild(document.createTextNode('New Element'));
parent.insertBefore(newElement, parent.children[i]);
newElement = document.createElement('div');
newElement.appendChild(document.createTextNode('New Element 2'));
parent.insertBefore(newElement, parent.children[i + 2]);
}
}
window.addEventListener('load', init);
with vanilla javascript you can do this
var parentElement = document.getElementById("parentElement");
// clone children array
var children = Array.prototype.slice.call( parentElement.children )
// get the length of children
var length = children.length;
for(var i = 0;i <= length;i++){
/*get current child*/
var currentChild = children[i];
/*create new element */
var newElement = document.createElement("div");
/*give it class*/
newElement.className = "newElem"
if(i == length){
/*append the element if we reach the end of loop*/
parentElement.appendChild(newElement)
}else{
/*use the insertBefore method*/
parentElement.insertBefore(newElement , currentChild)
}
}
/*log the result working 100%*/
console.log( parentElement )
result we get
The approach I'd take, making use of before() and after():
Codepen
var newElem = "<div class='newElem'></div>";
$('#parentElement div').each(function (index, element) {
if (index == 0) {
element.before(newElem);
}
element.after(newElem);
});
if ($('#parentElement div').length == 0) {
$('#parentElement').html(newElem);
}

Can't get correct dynamically added divs count

I have a body element, and in this body element I have a child element with id fullpage. And this element contains child element with classname section. In function load_works() I add two more section elements
<body>
<div id="fullpage">
<div class="section">
<!-- Content -->
</div>
</div>
<script src="js/main.js" charset="utf-8"></script>
<script type="text/javascript">
load_works();
</script>
</body>
Contents of main.js:
function load_works() {
var container = document.getElementById('fullpage');
for (var i = 0; i < 2; i++) {
var new_section = document.createElement('div');
new_section.className = "section";
container.appendChild(new_section);
}
}
The problem is, that when I try to count section elements with getElementsByClassName() like this after load_works() is executed
function some_function() {
var sections = document.getElementsByClassName('section');
console.log(sections.length);
}
it always returns 1, and this, I think, is because I have only one static section element. querySelectorAll() of course and other get- fucntions also aren't working. So, how can I achieve the correct result with pure Javascript, not jQuery?
You better should place all javascript inside one file like main.js and then you only need to call some_function() after load_works().
function load_works() {
var container = document.getElementById('fullpage');
for (var i = 0; i < 2; i++) {
var new_section = document.createElement('div');
new_section.className = "section";
container.appendChild(new_section);
}
}
function some_function() {
var sections = document.getElementsByClassName('section');
alert(sections.length);
}
load_works();
some_function();
<div id="fullpage">
<div class="section">
<!-- Content -->
</div>
</div>

Sort an array with DOM tags and attributes by left and top values

I have got an array with DOM tags (div -> children img -> div closed). Those tags has attributes (left and top values). I added em as nodeLists so i can access elements offsetLeft and offsetTop to determine those values but i cant figurate out how to rearange those tags in the array ascending by left or top values.
HTML
<body>
<div style="left:40px;top:10px;position:absolute">
<img src="assets/images/text1.png" />
</div>
<div style="left:200px;top:88px;position:absolute">
<img src="assets/images/text2.png" />
</div>
<div style="left:85px;top:166px;position:absolute">
<img src="assets/images/text3.png" />
</div>
</body>
JavaScript
var unsortedElements = [];
var sortedElements = [];
var elements = document.querySelectorAll("body > div");
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (element != null) {
unsortedElements.push(element);
var x = element.offsetLeft;
var y = element.offsetTop;
customSort(unsortedElements[i]);
}
}
function customSort(element){
var left = element.offsetLeft;
sortedElements.push(left);
console.log(sortedElements); -> will return the left values (40,85,200) BUT i need this array sorted with div tags (like [<div style="left: 40px;">img child</div>, <div style="left: 85px;">img child</div>, <div style="left: 200px;">img child</div>])
}
Note
Pure Javascript please!
Thank you :)
With ES6, you get a new bunch of functions to have the job done more elegantly.
For the purpose of that update, i'm just going to sort for the left property.
I guess the replacement of a comparator function is straightforward from now on.
HTML
<caption>Sorting by left offset</caption>
<div id="container" style="position:relative">
<div class="candidate" style="left:200px;top:88px;position:absolute">
<span>3 was 1 before sorting</span>
</div>
<div class="candidate" style="left:40px;top:10px;position:absolute">
<span>1 was 2 before sorting</span>
</div>
<div class="candidate" style="left:85px;top:166px;position:absolute">
<span>2 was 3 before sorting</span>
</div>
</div>
Pure JavaScript / EcmaScript 2015
Array.from(document.querySelectorAll("div.candidate")).sort(function(a, b){
let aLeft = a.style.left.replace("px","");
let bLeft = b.style.left.replace("px","");
return aLeft-bLeft;
}).forEach(function(el){
document.querySelectorAll("#container")[0].appendChild(el);
});
JSFiddle
http://jsfiddle.net/a4e41pmr/
I used appendChild as a trick to move the nodes once sorted.
You can use high order function to create custom filter, in order to filter DOM element.
You can do the following :
var elements = document.querySelectorAll('div');
//Create our function generator
function sortBy(prop){
return function(a, b){
var filter_a = parseInt( a.style[prop] );
var filter_b = parseInt( b.style[prop] );
return filter_a < filter_b
? -1
: (filter_a > filter_b ? 1 : 0);
}
}
function sortDom(filter){
//Transform our nodeList into array and apply sort function
return [].map.call(elements, function(elm){
return elm;
}).sort(sortBy(filter))
}
//Sort by left style property
var byLeft = sortDom('left');
//Sort by top style property
var byTop = sortDom('top');
You could use the sort function of javascript arrays. Unfortunately that doesn't work on nodelists (which you get from calling querySelectorAll), so you have to do some converting:
var elements = document.querySelectorAll("body > div");
var divs=[];
for (var i=0;i<elements.length;i++) {
divs.push(elements[i]);
}
This puts all your elements into an array. Now for the sorting:
divs.sort(function(a,b){
var aLeft = a.style.left.replace("px","");
var bLeft = b.style.left.replace("px","");
return aLeft-bLeft;
});
This sorts by the left style attribute, provided that they're all in the form 123px.
In practice I would probably do it as #Paul Boutes suggested as it's more generic, but I thought that this way might be easier to understand.

Pass 'div' element as parameter to JavaScript function

I need to create, decorate and append child 'divs' to existing. For example, after the appendChildren is executed, following divs
<div id="a">
<div id="b">
</div>
</div>
should take the following form (assuming decorateDiv adds text "This is new div" inside new div)
<div id="a">
<div id="b">
<div>"This is new div"</div>
</div>
<div>"This is new div"</div>
</div>
Here is my code
function appendChildren() {
var allDivs = document.getElementsByTagName("div");
for (var i = 0; i < allDivs.length; i++) {
var newDiv = document.createElement("div");
decorateDiv(newDiv);
allDivs[i].appendChild(newDiv);
}
}
function decorateDiv(div) {
var x = document.getElementByTagName("div");
var t = document.createTextNode("This is new div");
x.appendChild(t);
}
I am completely new to JavaSpript. What am I doing wrong? Please help me to fix bugs
You're not using the parameter div and the decorateDiv should look like this:
function decorateDiv(div) {
//div is the object element
var t = document.createTextNode("This is new div");
div.appendChild(t);
}
The parameter 'div' that you are passing to function 'decorateDiv' has nowhere been used.
You can change your decorateDiv function like as below:
function decorateDiv(div) {
var t = document.createTextNode("This is new div");
div.appendChild(t);
}
Hope this helps!!
"allDivs" variable get update when to appendChild to any div because to stores array of elements of "div" TagName.
So, use class for fetching divs.
<div id="a" class="test">
A
<div id="b" class="test">
B
</div>
</div>
Here is user script:
function appendChildren() {
var allDivs = document.getElementsByClassName("test");
for (var i = 0 ; i < allDivs.length ; i++) {
var newDiv = document.createElement("div");
decorateDiv(newDiv);
allDivs[i].appendChild(newDiv);
}
}
function decorateDiv(div) {
var t = document.createTextNode("This is new div");
div.appendChild(t);
}
appendChildren();
And also you are not using parameter passed to decorative function.
This will work. Try this..

JS Remove Every Other Element

I tried this code to remove every other element in the body, but it didn't work.
s = document.body.childNodes;
for (var i = 1; i < (s.length / 2); i++) {
if ((i % 2) == 0) {
document.body.removeChild(s[1]);
} else {
document.body.removeChild(s[0]);
}
}
<body>
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>
<div id="div4"></div>
<div id="div5"></div>
<div id="div6"></div>
</body>
There are a number of problems with your code. For one thing the logic is off, but there are some major problems with how the code deals with the DOM manipulations:
Your HTML causes text nodes to be created between your elements. And your code does not handle this.
The childNodes list changes as you remove nodes from the parent element.
With this HTML:
<div id="test-container">
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>
<div id="div6">6</div>
</div>
And this JavaScript:
var container = document.getElementById("test-container");
var child = container.firstElementChild;
var remove = true;
while (child) {
var next = child.nextElementSibling;
if (remove)
container.removeChild(child);
remove = !remove;
child = next;
}
I can remove every other child. I avoid both problems I pointed out earlier by using firstElementChild and nextElementSibling.
First, you need to get the elements using children or querySelectorAll() not childNodes, childNodes will get all nodes including the text. Try the following code:
var s = document.body.children;
var itemCount = s.length;
for (var i = 0; i < itemCount; i++)
{
if (i % 2 !== 0)
{
document.body.removeChild(s[i]);
}
}
Here is a JSBin example.
Note that in JSBin we get the elements using querySelectorAll(), beacuse it also adds script elemets inside the body, for example:
var s = document.querySelectorAll('div');
Also, note that IE8 and below includes comment nodes when using children.
You need to know that a nodeList will update when the dom is updated, that means that if you are removing the first child node, the element 0 in the list will not refer to null but the node that was initial child 1.
This means that if you want to remove node 0, 2, 4, 6, ... you will actually have to remove 0,1,2,3,4... (because the nodeList always will update):
var body = document.body;
// Consider that you might want to use `body.children` instead of
// `body.childNodes` because it excludes all text nodes
var nodes = body.childNodes;
var len = nodes.length / 2;
for ( var i = 0; i < len; i++ ) {
body.removeChild(nodes[i]);
}
I know it can seams kind of odd, but this will actually remove all the nodes: 0, 2, 4, 6, ...
See more at MDN
I'd just use querySelectorAll with a :nth-child(odd) selector:
var divs = document.querySelectorAll('body > div:nth-child(odd)');
divs = [].slice.call(divs); // convert from NodeList to real array
divs.forEach(function(elem) {
document.body.removeChild(elem);
});
jsFiddle
Why not use jQuery and simply do:
$('.remOdd').on('click', function(){
$('div').filter(':odd').remove();
})
http://jsfiddle.net/dh5uymzL/
Or of course use ":even" if you like
http://api.jquery.com/filter/

Categories