I'm new to Javascript and I'm having a problem with a closing div to a loop after every 3 divs in order to restart the column of divs. Although it clearly adds the new div , Javascript refuses to add the closing div (see code below). Thank you in advance for your help.
var cats = 8;
var cat= '';
for(var i=1; i<=cats; i ) {
cat = "<div class=\"cat three_columns two_columns\">" i "</div>";
if(i%3==0) {cat = "</div><div class=\"column\">";}
}
var threecolumns = document.getElementsByClassName('column');
threecolumns[0].innerHTML = cat;
<style type="text/css">
* { box-sizing: border-box;}
.columnsBox {width:90%; padding:10px; margin:0 auto; border:1px solid green; overflow:auto;}
.column {float:left; width:100%; padding-right:8px;}
.cat {font-size:16px; text-align:center; margin-bottom:10px;padding:12px; color:#5a2e0f; background-color:#d7d7d7; border:1px solid #c1bfbf; outline:none; cursor:pointer;}
</style>
<div style="width:90%; min-width:200px; margin:0 auto; padding:10px; ">
<div >
<div ></div>
This is where I placed the javascript
</div>
</div>
Desired Output:
<div >
<div >
<div >1</div>
<div >2</div>
<div >3</div>
</div>
<div >
<div >4</div>
<div >5</div>
<div >6</div>
</div>
<div >
<div >7</div>
<div >8</div>
</div>
</div>
CodePudding user response:
The string you pass to innerHTML
must be a self-contained piece of HTML. It cannot close a tag that is not opened in it. Such closing </div>
is ignored.
To overcome this, I would suggest creating the elements with createElement
:
let cats = 8;
let div;
let container = document.querySelector(".columnsBox");
for (let i = 1; i <= cats; i ) {
if (i % 3 == 1) {
column = document.createElement("div");
column.className = "column";
container.appendChild(column);
}
let child = document.createElement("div");
child.className = "cat three_columns two_columns";
child.textContent = i;
column.appendChild(child);
}
* {
box-sizing: border-box;
}
.columnsBox {
width:90%;
padding:10px;
margin:0 auto;
border:1px solid green;
overflow:auto;
}
.column {
float:left;
width:100%;
padding-right:8px;
}
.cat {
font-size:16px;
text-align:center;
margin-bottom:10px;
padding:12px;
color:#5a2e0f;
background-color:#d7d7d7;
border:1px solid #c1bfbf;
outline:none;
cursor:pointer;
}
.three_columns {
width: 30%;
display: inline-block;
margin: 5px;
}
<div style="width:90%; min-width:200px; margin:0 auto; padding:10px; ">
<div >
</div>
</div>
CodePudding user response:
Here's another way. Use 2 loops. I show the output as <pre>
html so you can see the structure.
let cats = 8,
numCols = 3,
x = 0,
catsPerCol = Math.max(cats / numCols),
output = "";
for (let r = 0; r < numCols; r ) {
output = `<div >\n`;
for (let c = 0; c < catsPerCol; c ) {
if (x < cats) output = ` <div >${x}</div>\n`;
}
output = `</div>\n`;
}
document.querySelector('.columnsBox').innerText = output;
<pre ></pre>
CodePudding user response:
Solution using a generator function
You can also use a generator function to create the same result. This requires some more advanced JavaScript and is not the most efficient solution (will be irrelevant for most use cases), but can be quite an elegant solution without the need to use the modulo operator.
// wait for HTML to load
window.addEventListener("DOMContentLoaded", (e) => {
// get parent component which holds all columns
const colBox = document.querySelector(".columnsBox");
// number of total categories
const cats = 8;
// number of categories per column
const catsPerColumn = 3;
// create all categories
const allDivs = [...new Array(cats)].map((_, idx) => createCategory(idx 1));
// create a column for every catsPerColumn elements
for (const columnChildren of chunksArray(allDivs, catsPerColumn)) {
const newCol = createColumn(columnChildren);
colBox.appendChild(newCol);
}
});
/**
* Creates a category div
* @param {number} id ID for the column
* @returns div within a column
*/
function createCategory(id) {
const newCol = document.createElement("div");
// add classes to this div
newCol.classList.add("cat", "three_columns", "two_columns");
newCol.textContent = id;
return newCol;
}
/**
* Create a column containing children
* @param {Iterable<HTMLElement>} columnChildren
* @returns column with children
*/
function createColumn(columnChildren) {
const newColumn = document.createElement("div");
// add class to this div
newColumn.classList.add("column");
// all all divs that belong to that column to it
newColumn.append(...columnChildren);
// add column to column box
return newColumn;
}
/**
* Generator function which returns all elements of an array in chunks of a specified size (or smaller if there are not enough values in array)
* @param {Array<any>} array an array
* @param {number} chunkSize size of one chunk
*/
function* chunksArray(array, chunkSize) {
let cur = 0;
do {
// create a new array only holding the values from the current value till current value chunkSize or till the end of the array
yield array.slice(cur, Math.min(cur chunkSize, array.length));
cur = chunkSize;
} while (cur <= array.length);
}
* {
box-sizing: border-box;
}
.columnsBox {
width: 90%;
padding: 10px;
margin: 0 auto;
border: 1px solid green;
overflow: auto;
}
.column {
float: left;
width: 100%;
padding-right: 8px;
border: 5px solid black;
}
.cat {
font-size: 16px;
text-align: center;
margin-bottom: 10px;
padding: 12px;
color: #5a2e0f;
background-color: #d7d7d7;
border: 1px solid #c1bfbf;
outline: none;
cursor: pointer;
}
<div >
Please note: I've added a thick black border to show which
<div>
s belong to the same column.