Wrapping elements while looping through HTMLCollection causes problem - javascript

I want to wrap each item of the container in a div. When I loop through HTMLCollection, some elements are accessed multiple times while others are left out
HTML
<div class="container">
<div class="item_1"></div>
<div class="item_2"></div>
<div class="item_3"></div>
<div class="item_4"></div>
<div class="item_5"></div>
<div class="item_6"></div>
<div class="item_7"></div>
<div class="item_8"></div>
<div class="item_9"></div>
</div>
JS
const container = document.querySelector('.container');
const items = container.children;
for(let i = 0; i < items.length; i++) {
const wrapper = document.createElement('div');
wrapper.classList.add('wrapper');
wrapper.appendChild(items[i]);
container.appendChild(wrapper);
}
Looping directly through HTMLCollection gives this bizarre result
<div class="container">
<div class="item_2"></div>
<div class="item_4"></div>
<div class="item_6"></div>
<div class="item_8"></div>
<div class="wrapper">
<div class="item_1"></div>
</div>
<div class="wrapper">
<div class="item_5"></div>
</div>
<div class="wrapper">
<div class="item_9"></div>
</div>
<div class="wrapper">
<div class="wrapper">
<div class="item_7"></div>
</div>
</div>
<div class="wrapper">
<div class="wrapper">
<div class="wrapper">
<div class="wrapper">
<div class="item_3"></div>
</div>
</div>
</div>
</div>
</div>
problem gets solved when I convert HTMLCollection to an Array
const items = Array.from(container.children);
I can't understand what causes such behavior

You were iterating the container.children list which you were also changing during the iterations. This messed up the iteration. You can solve this, as you mentioned yourself, by converting the container.children to an array because then you are not iterating over the live container.children list but over an array copy of that. This copy is still referring to the correct child elements so they are moved correctly with the appendChild() function.
As an alternative you can use the querySelecterAll() to retrieve all the elements you want to wrap.
const container = document.querySelector('.container');
const items = container.querySelectorAll('.container > *');
for(let i = 0; i < items.length; i++) {
const wrapper = document.createElement('div');
wrapper.classList.add('wrapper');
wrapper.appendChild(items[i]);
container.appendChild(wrapper);
}
.wrapper {
background-color: red;
}
<div class="container">
<div class="item_1">1</div>
<div class="item_2">2</div>
<div class="item_3">3</div>
<div class="item_4">4</div>
<div class="item_5">5</div>
<div class="item_6">6</div>
<div class="item_7">7</div>
<div class="item_8">8</div>
<div class="item_9">9</div>
</div>

Related

changing Div order in a div main container, javascript DOM manipulation

i want to move a div form the start to the end in a the same div:from 1-2-3 to 2-3-1
my code
const cards = document.querySelectorAll(".card");
const firstCard = document.querySelectorAll(".card")[0].innerHTML;
cards[0].remove();
document.getElementById("mainC").appendChild(firstCard);
<div id="mainC">
<div class="card"> 1 </div>
<div class="card"> 2 </div>
<div class="card"> 3 </div>
</div>
i want to move a div form the start to the end in a the same div:from 1-2-3 to 2-3-1
Based on your original code,we need to remove .innerHTML,then it will work
const cards = document.querySelectorAll(".card");
const firstCard = document.querySelectorAll(".card")[0];// remove .innerHTML and it will work
cards[0].remove();
document.getElementById("mainC").appendChild(firstCard);
<div id="mainC">
<div class="card"> 1 </div>
<div class="card"> 2 </div>
<div class="card"> 3 </div>
</div>
Another solution is to store the content into an array and change the array element order
let divs = []
document.querySelectorAll('#mainC .card').forEach(d =>{
divs.push(d.outerHTML)
})
divs.push(divs.shift())
document.querySelector('#mainC').innerHTML = divs.join('')
<div id="mainC">
<div class="card"> 1 </div>
<div class="card"> 2 </div>
<div class="card"> 3 </div>
</div>
you have used document.querySelectorAll(".card")[0].innerHTML which gives '1' which is not type "node" so it will give an error when appending as a child.
remove .innerHTML and it will work
here is an example that removes the first child and append it to the end.
const shuffle = () => {
const parent = document.querySelector("#mainContainer");
const childrens = [...parent.children];
parent.appendChild(childrens.splice(0,1)[0]);
};
<button type="button" onclick=shuffle()> suffel</button>
<div id="mainContainer">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
</div>

Vanilla Javascript insertBefore() in for loop

I have the following html structure.
<div id="page1" class="page">
<div class="firstchild"></div>
<div class="secondchild"></div>
<div class="thirdchild"></div>
<div class="forthchild"></div>
</div>
<div id="page2" class="page">
<div class="firstchild"></div>
<div class="secondchild"></div>
<div class="thirdchild"></div>
<div class="forthchild"></div>
</div>
<div id="page3" class="page">
<div class="firstchild"></div>
<div class="secondchild"></div>
<div class="thirdchild"></div>
<div class="forthchild"></div>
</div>
And my javascript structure is.
var pageCLASS = frame.querySelectorAll(".page");
//var pageCLASS = frame.getElementsByClassName('page')[0];
var leng = pageCLASS.length;
for (var i = 0; i < leng; ++i) {
var pageID = frame.getElementById('page' + (i + 1));
var firstchild = frame.getElementsByClassName('firstchild')[0];
var secondchild = frame.getElementsByClassName('secondchild')[0];
var thirdchild = frame.getElementsByClassName('thirdchild')[0];
pageID.insertBefore(thirdchild, firstchild.nextSibling);
//pageCLASS.insertBefore(thirdchild, firstchild.nextSibling);
}
Now I have problems with the thirdchild being moved to below the firstchild and above the secondchild in all of page1, page2, and page3 together. The code above only moves it in page1, but for the other 2 which does nothing. The frame shown in the source is an iframe stored on the same domain with access to it's elements. Can I please get some advice on what I am doing wrong as I want to move all thirdchilds in each div to below the first child in each of their parent div?
The problem you are having is that you are constantly targeting the same elements with e.g.
var firstchild = frame.getElementsByClassName('firstchild')[0];
because this instruction always returns the first occurrence of such an element in the iframe and never the second or third.
In order to be sure that you are targeting the correct elements you can rewrite some of your code to only search for the elements that are contained within a certain parent and not inside the whole iframe.
You can then use something like this instead
var firstChild = pageID.querySelector('.firstchild');
which will only search for an element (the first occurrence) with class firstchild that is contained within some other element (in this case the element saved in pageID).
Check below (I exchanged the form for document so we can test here):
var pageCLASS = document.querySelectorAll(".page");
var leng = pageCLASS.length;
for (var i = 1; i <= leng; i++) {
var pageID = document.getElementById('page' + i);
var firstChild = pageID.querySelector('.firstchild');
var thirdChild = pageID.querySelector('.thirdchild');
firstChild.parentNode.insertBefore(thirdChild, firstChild.nextSibling);
}
.page {
border: 1px solid #09f;
}
<div id="page1" class="page">
<div class="firstchild">first child</div>
<div class="secondchild">second child</div>
<div class="thirdchild">third child</div>
<div class="forthchild">fourth child</div>
</div>
<div id="page2" class="page">
<div class="firstchild">first child</div>
<div class="secondchild">second child</div>
<div class="thirdchild">third child</div>
<div class="forthchild">fourth child</div>
</div>
<div id="page3" class="page">
<div class="firstchild">first child</div>
<div class="secondchild">second child</div>
<div class="thirdchild">third child</div>
<div class="forthchild">fourth child</div>
</div>

Convert rows of Div values into an array with jQuery

Example:
I have a variable amount of rows, each with 3 divs with classes like so:
<div class="row>
<div class='date'>1/2/2018</div>
<div class='event'>concert</div>
<div class='act'>Pink Floyd</div>
</div>
<div class="row>
<div class='date'>12/5/2017</div>
<div class='event'>dj set</div>
<div class='act'>Moby</div>
</div>
<div class="row>
<div class='date'>5/5/2018</div>
<div class='event'>movie</div>
<div class='act'>Ant-Man</div>
</div>
How can I use jQuery to gather them into an associative array that I'll be passing to php to INSERT into a database table?
You can use map to loop thru the .row div. Use reduce to group the inner divs into a js object.
var result = $('.row').map(function() {
return $(this).find('div').toArray().reduce(function(c, v) {
c[$(v).attr('class')] = $(v).text();
return c;
}, {});
}).get();
console.log(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row">
<div class='date'>1/2/2018</div>
<div class='event'>concert</div>
<div class='act'>Pink Floyd</div>
</div>
<div class="row">
<div class='date'>12/5/2017</div>
<div class='event'>dj set</div>
<div class='act'>Moby</div>
</div>
<div class="row">
<div class='date'>5/5/2018</div>
<div class='event'>movie</div>
<div class='act'>Ant-Man</div>
</div>
To select each all the div with the class row use this
var rows=$('div.row');
To loop through it use this
var array=[];
for(var i=0;i <rows.length;i++){
$.each(rows,function(){
var children=this.children('div');
$.each(children,function(){
var index=this.attr('class');
var value=this.text();
array[i][index]=value;
});
});
}
console.log(array);

How to remove div container from HTML but not its content

I have this structure:
<div clas="page_cat_list">
<div class="page_cat_row">
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="clear_fix"></div>
</div>
<div class="page_cat_row">
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="clear_fix"></div>
</div>
</div>
and I need it to look like this:
<div clas="page_cat_list">
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
</div>
I can insert JavaScript in the HTML, but I can not change these elements directly, because it's generated by some kind of function.
You could arrive with this simple js approach:
var container = document.querySelector('.page_cat_list');
var contents = container.querySelectorAll('.page_cat_row');
var newContent = '';
[].forEach.call(contents, function(cont) {
newContent = newContent + cont.innerHTML;
})
container.innerHTML = newContent;
<div class="page_cat_list">
<div class="page_cat_row">
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="clear_fix"></div>
</div>
<div class="page_cat_row">
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="page_cat_item">...</div>
<div class="clear_fix"></div>
</div>
</div>
Loop over the elements and than loop over the children appending them to the parent element.
(function() {
var wrappers = document.querySelectorAll(".page_cat_row"); //grab the parent elements
//Loop over the collection
for (var i = wrappers.length - 1; i >= 0; i--) {
//get the element and its parent
var wrapperToBeRemoved = wrappers[i],
parentNode = wrapperToBeRemoved.parentNode
//loop over the children until they are all removed
while (wrapperToBeRemoved.firstChild) {
parentNode.insertBefore(wrapperToBeRemoved.firstChild,
wrapperToBeRemoved);
}
//remove the wrapper from the collection
parentNode.removeChild(wrapperToBeRemoved);
}
}());
.page_cat_row {
background-color: red;
}
<div clas="page_cat_list">
<div class="page_cat_row">
<div class="page_cat_item">1...</div>
<div class="page_cat_item">2...</div>
<div class="page_cat_item">3...</div>
<div class="clear_fix"></div>
</div>
<div class="page_cat_row">
<div class="page_cat_item">4...</div>
<div class="page_cat_item">5...</div>
<div class="page_cat_item">6...</div>
<div class="clear_fix"></div>
</div>
</div>
You can easily do this with a combination of detach() and html() in JQuery:
$(document).ready(function(){
var ele = $('.page_cat_row').children().detach();
$('.page_cat_row').remove();
$('.page_cat_list').html(ele);
});
See the JSFiddle: https://jsfiddle.net/w6gkf23d/
With this, you don't need to directly loop anything as JQuery does it all for you.
Note that you will still need to set the clear_fix class to display:none.
Simplest way is to use unwrap.
$('.page_cat_item').unwrap();
I think this is the easiest way to do this.
Move the first childNode to the parent until the wrapper element has children.
If you don't have text between the elements, you can use wrapper.children.length instead, because it's faster in that case.
function removeElement(element) {
element.parentElement.removeChild(element);
}
function removeWrapper(wrapper) {
while (wrapper.childNodes.length) {
wrapper.parentElement.appendChild(wrapper.childNodes[0]);
}
wrapper.parentElement.removeChild(wrapper);
}
var clrFix = document.querySelectorAll('.page_cat_row > .clear_fix');
for (var i = 0; i < clrFix.length; i++) {
removeElement(clrFix[i]);
}
var cat_rows = document.querySelectorAll('.page_cat_list > .page_cat_row');
for (var i = 0; i < cat_rows.length; i++) {
removeWrapper(cat_rows[i]);
}
.page_cat_row {
color: red;
}
<div class="page_cat_list">
<div class="page_cat_row">
<div class="page_cat_item">1...</div>
<div class="page_cat_item">2...</div>
<div class="page_cat_item">3...</div>
<div class="clear_fix">fix</div>
</div>
<div class="page_cat_row">
<div class="page_cat_item">4...</div>
<div class="page_cat_item">5...</div>
<div class="page_cat_item">6...</div>
<div class="clear_fix">fix</div>
</div>
</div>
JSFiddle
$( "#divid" ).load(function() {
$( this ).removeClass( "page_cat_row" );
});

How to push element attribute value into javascript array?

This is my html structure. Here, i have created two attributes for each element. How to push attribute value into an array (like this structure, [[1,0],[1,1],[1,2],[0,2],[2,2],[1,3],[1,4]])?
<div id="demo">
<div data-floor="1" data-floor-sub="0">Div1</div>
<div data-floor="2" data-floor-sub="0">Div2</div>
<div data-floor="3" data-floor-sub="0">Div3</div>
<div data-floor="3" data-floor-sub="1">Div4</div>
<div data-floor="3" data-floor-sub="2">Div5</div>
<div data-floor="4" data-floor-sub="0">Div6</div>
<div data-floor="5" data-floor-sub="0">Div7</div>
</div>
Use map()
var multiDimArray = $('#demo div').map(function() {
var innerArray = [];
innerArray.push([$(this).data('floor'), $(this).data('floor-sub')]);
return innerArray;
}).get();
console.log(multiDimArray);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<div id="demo">
<div data-floor="1" data-floor-sub="0">Div1</div>
<div data-floor="2" data-floor-sub="0">Div2</div>
<div data-floor="3" data-floor-sub="0">Div3</div>
<div data-floor="3" data-floor-sub="1">Div4</div>
<div data-floor="3" data-floor-sub="2">Div5</div>
<div data-floor="4" data-floor-sub="0">Div6</div>
<div data-floor="5" data-floor-sub="0">Div7</div>
</div>

Categories

Resources