SO-72132063
by David Thomas
HTML
<main>
<h2>Custom columns:</h2>
<label>
<input type="number" min="1" step="1" value="1">
<span>Set number of columns</span>
</label>
<button>Add new element</button>
<div class="multicol" style="--columns: 3">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</main>
CSS
/* custom properties used through the stylesheet: */
:root {
--columns: 1;
--colGap: 0.5em;
--rowGap: 0.25em;
}
/* a hugely naive, simple CSS reset/normalisation to
ensure consistent base-styles for cross-browser
normalisation: */
*,
::before,
::after {
box-sizing: border-box;
font-family: Roboto, Montserrat, system-ui;
font-size: 16px;
font-weight: 400;
margin: 0;
padding: 0;
}
main {
display: grid;
gap: 1em;
margin-block: 1em;
margin-inline: auto;
/* setting an ideal width of 60vw (60 percent of the viewport-width,
with a minimum size of 20em and a maximum size of 800px: */
width: clamp(20em, 60vw, 800px);
}
h2 {
font-size: 1.2em;
font-weight: 600;
text-align: start;
}
label,
div {
border: 1px solid #000;
padding: 0.5em;
}
label {
/* a habit from positioning the label-text after the associated <input>
in order to style that text based on status of the <input>, but not
needed in this instance: */
display: flex;
flex-direction: row-reverse;
gap: var(--colGap);
align-items: center;
justify-content: center;
}
/* garish, but it does ensure that people can see when the <input> is focused: */
label:focus-within {
background-image: linear-gradient(45deg, hsl(0 100% 50% / 0.4), hsl(60 100% 50% / 0.6));
}
label input {
padding-block: var(--rowGap);
padding-inline-start: var(--colGap);
}
label span::after {
content: ':';
}
main>div {
margin: auto;
}
.multicol {
counter-reset: spanCount;
/* setting the number of columns in the element to be equal
to the --columns custom-property, or a default-value of 1: */
column-count: var(--columns, 1);
column-gap: var(--colGap);
max-width: 100%;
width: 100%;
}
.multicol span {
display: block;
padding-block: var(--rowGap);
}
.multicol span:nth-child(odd) {
background-color: palegreen;
}
.multicol span::before {
counter-increment: spanCount;
content: counter(spanCount);
}
JavaScript
// retrieves the first/only <input> element in the document of "type=number":
const input = document.querySelector('input[type=number]'),
// retrieves the first/only element in the document with the class of "multicol":
multicol = document.querySelector('.multicol');
// we set the value of the <input> to the value held in the current `--columns`
// custom property:
input.value = multicol.style.getPropertyValue('--columns');
// we select all <button> elements in the document, and iterate over that
// NodeList using NodeList.prototype.forEach():
document.querySelectorAll('button').forEach(
// we use EventTarget.addEventListener() to bind an anonymous Arrow
// function as the event-handler for the 'click' event on the <button>
// element(s):
(btn) => btn.addEventListener('click', () => {
// creating a <span> element:
let span = document.createElement('span'),
// from the btn (<button>) element passed into the function-body
// of the Arrow function we navigate to the parent-node of that
// <button> element, and from there we find the '.multicol'
// element:
spanParent = btn.parentNode.querySelector('.multicol');
// we then append the new <span> to that element:
spanParent.append(span);
})
);
// here we bind an anoymous Arrow function as the event-handler for a
// 'change' event on the <input>:
input.addEventListener('change', (e) => {
// we get a reference to that <input>, using the Event.currentTarget()
// property:
let changed = e.currentTarget,
// from there we navigate from the <input> to the closest <main>
// element, and retrieve the first/only element with a class of
// "multicol":
columnElement = changed.closest('main').querySelector('.multicol');
// we then use the CSSStyleDeclaration.setProperty() method, to update
// the value of '--columns' custom CSS property to the current value
// of the <input>:
columnElement.style.setProperty('--columns', changed.value);
});