I'm trying to make a masonry layout and the logic is as follows: I have an array of elements [elem1,elem2,elem3,elem4] and I want to distribute them in 3 columns, but they must appear in this order:
columna1 columna2 columna3
elem1 elem2 elem3
For some reason the first element is not added, I don't understand why
this is my javascript code:
I don't understand why not all the elements are added, I even tried with an array of numbers to see the logic and the numbers were added correctly
let articles = document.getElementsByClassName("new-article")
let columns = [document.createElement("div"),
document.createElement("div"),
document.createElement("div")]
let longElements = articles.length-1
let elementsCounter = 0
for(let i=0; i<columns.length; i++){
document.getElementById("container-articles").appendChild(columns[i])
}
for(let i=0; i<columns.length; i++){
columns[i].classList.add("col-xl-4")
}
for(let i=0; i<=columns.length; i++){
if(i===columns.length){
i=0;
}
if(longElements<0){
break
}
columns[i].appendChild(articles[elementsCounter])
longElements--
elementsCounter++
}
Here I test the algorithm with numbers before using the DOM
let elementos = [12,12,3,41,2]
let columnas = [[],[],[]]
let longElementos = elementos.length-1 //4
for(let i=0; i<=columnas.length; i++){ //i=0,1,2,3
if(i===columnas.length){ // i=0,1,2,3
i=0
}
if(longElementos<0){ // longElementos 2,41,3,12,12
break
}
columnas[i].push(elementos[longElementos]) //3,2,1,0
longElementos--
}
The output is this, which is just what I expect:
arr1---> 0: [2, 12]
arr2---> 1: [41, 12]
arr3---> 2: [3]
Update:
Here is the HTML that I am using
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"
type="text/css">
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
<article class="new-article">
<img src="http:lorempixel.com/500/500">
<h1>Noticia 1</h1>
</article>
<article class="new-article">
<img src="http:lorempixel.com/500/500">
<h1>Noticia 2</h1>
</article>
<div class="container-fluid">
<div class="row" id="container-articles">
</div>
</div>
<script src="masonry.js"></script>
</body>
</html>
Your problem is that you are iterating through the array
articles
at the same time that you are moving the elements that are inside it within the HTML.By definition, the function
getElementsByClassName
returns aHTMLCollection
which is an object representing a list of nodes. This list is ordered (unless otherwise indicated) respecting the order of the HTML tree. Furthermore, this list is alive , which means that any changes to it will alter the list .In your case, let's look at the last for (in which you assign the articles to the different columns) what is happening is the following:
In the first iteration the array
articles
is ordered [News 1, News 2] and the variable isi=0
. You access thecolumna[0]
and thearticles[0]
and News 1 becomes inside thecontainer-articles
. Everything is perfect.Let's see what happens in the next iteration of the array. The array
articles
-remember that it is a live list and that it respects the HTML order- this time will contain the values like this [News 2, News 1] since now News 1 is lower than News 2 in the HTML tree. However,i=1
for what it is going to position News 1 inside,container-articles
leaving News 2 outside of it.To fix it, I have created a variable
j=0
that I reset in each iteration of the loop and that I will use to get the articles. I always get the article that is in position 0 since the array is orderedarticles
according to the order that the HTML has and since each time we position an element we set it lower than the articles within the order of the HTML, as I move the items. different items the next item I have to move will always be the first item in the array.In addition, I check to see if there are more columns than items, in which case I control the error that would occur when trying to insert a child that does not exist in a column.
Note: I have removed your variables
longElements
andelementsCounter
since I have not seen them necessary.Your corrected code: