<ol class="container">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
</ol>
<div class="controls">
<div class="formula">
<code>
<pre><span id="factor-limit">3</span> * <span id="--si2">0</span> + <span id="--si1">0</span></pre>
</code>
<code class="result">
<span id="current">0</span>
<span id="max">(8 max)</span>
</code>
</div>
<form class="sliders">
<label for="elements">
Elements: <span class="value">9</span>
</label>
<input type="range" id="elements" min="2" max="49" value="9">
<label for="factor">
Factor: <span class="value">3</span>
</label>
<input type="range" id="factor" min="2" max="7" value="3">
</form>
<main class="snippets">
<div class="sibling-index" id="snippets-si1">
<h2>--si1:</h2>
<code>
<pre data-sibling-index="1" data-value="1">li:nth-child(3n + 1){ --si1: 1;}</pre>
</code>
<code>
<pre data-sibling-index="1" data-value="2">li:nth-child(3n + 2){ --si1: 2;}</pre>
</code>
</div>
<div class="sibling-index" id="snippets-si2">
<h2>--si2:</h2>
<code>
<pre data-sibling-index="2" data-value="1">li:nth-child(n + 3 ):nth-child(-n + 5){--si2: 1;}</pre>
</code>
<code>
<pre data-sibling-index="2" data-value="2">li:nth-child(n + 6 ):nth-child(-n + 8){--si2: 2;}</pre>
</code>
</div>
</main>
</div>
/* Aesthetic Styles */
:root {
--text-color: #eae2b7;
--spacing: 25px;
color-scheme: dark;
}
* {
padding: 0px;
margin: 0px;
box-sizing: border-box;
}
html {
font-family: monospace;
font-size: clamp(10px, 1vw, 16px);
}
body {
display: flex;
justify-content: space-around;
min-height: 100vh;
background: repeating-linear-gradient(
-45deg,
#111,
#111 30px,
#222 30px,
#222 60px
);
}
.container {
flex: 1 1;
display: flex;
flex-flow: row wrap;
align-content: flex-start;
justify-content: flex-start;
gap: 15px;
padding: var(--spacing);
container-type: inline-size;
li {
display: grid;
place-items: center;
border-radius: 15px;
padding: 10px;
width: 60px;
aspect-ratio: 1;
list-style: none;
font-size: 1.5rem;
font-weight: 600;
color: var(--text-color);
background-color: black;
transition: all 100ms ease-in;
}
&:has(> :nth-child(30)) li {
width: 50px;
}
.selected {
transform: scale(110%);
background-color: orange;
}
.active {
background-color: purple;
}
}
.controls {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 3fr;
grid-template-areas:
"formula sliders"
"snippets snippets";
gap: var(--spacing);
height: 100vh;
scrollbar-gutter: stable both-edges;
padding: var(--spacing);
}
.snippets {
grid-area: snippets;
display: flex;
flex-flow: column;
gap: var(--spacing);
padding: var(--spacing);
border-radius: 20px;
overflow-y: auto;
scrollbar-gutter: stable both-edges;
background-color: #111;
color: var(--text-color);
box-shadow: 4px 4px 4px 4px #000;
.sibling-index {
display: flex;
flex-flow: column;
gap: var(--spacing);
}
pre {
margin: 0px 10px;
padding: 10px 40px 10px 20px;
border-radius: 10px;
width: 90%;
background-color: #171717;
transition: all 100ms ease-in;
cursor: pointer;
&:hover {
transform: scale(105%);
}
}
.active {
background-color: #000;
}
}
.formula {
grid-area: formula;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
gap: 20px;
padding: var(--spacing);
border-radius: 20px;
background-color: #111;
color: var(--text-color);
font-size: 1rem;
box-shadow: 4px 4px 4px 4px #000;
.result {
display: flex;
flex-flow: column;
align-items: center;
}
#max {
color: #999;
}
#current {
font-size: 4rem;
}
}
.sliders {
grid-area: sliders;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
gap: 10px;
padding: var(--spacing);
border-radius: 20px;
background-color: #111;
color: var(--text-color);
font-size: 1rem;
box-shadow: 4px 4px 4px 4px #000;
input {
width: 100%;
}
.value {
display: inline-block;
width: 2rem;
}
}
@media (width < 800px) {
body {
flex-flow: column;
justify-content: space-between;
}
.container li {
width: 5rem;
}
.controls {
grid-template-rows: 150px 160px;
height: auto;
}
.snippets {
justify-content: space-between;
width: 100%;
overflow: hidden;
.sibling-index {
align-items: center;
flex-flow: row;
gap: 20px;
}
pre {
visibility: hidden;
width: 0px;
height: 3rem;
padding: 0px;
}
pre::before {
content: attr(data-value);
display: grid;
place-items: center;
visibility: visible;
overflow: visible;
width: 3rem;
height: 3rem;
border-radius: 10px;
font-size: 1.3rem;
background-color: #171717;
transition: all 100ms ease-in;
cursor: pointer;
&:hover {
transform: scale(105%);
}
}
.active::before {
background-color: #000;
}
}
}
/* Selecting Elements */
const selectElements = (factorLimit) => {
let allElements = [];
for (let i = 1; i < factorLimit; i++) {
elementsSiblingIndex1 = document.querySelectorAll(
`li:nth-child(${factorLimit}n + ${i})`
);
allElements.push(elementsSiblingIndex1);
}
for (let i = 1; i < factorLimit; i++) {
elementsSiblingIndex2 = document.querySelectorAll(
`li:nth-child(n + ${factorLimit * i}):nth-child(-n + ${
factorLimit * (i + 1) - 1
})`
);
allElements.push(elementsSiblingIndex2);
}
return allElements;
};
const toggleSelectedElements = (elements) => {
elements.forEach((element) => {
element.classList.toggle("selected");
});
};
const removeAllActive = (elements) => {
elements.forEach((element) => {
element.classList.remove("active");
});
};
const changeActive = (elements, element) => {
if (!element) {
//If element is out of reach, simply remove all active
removeAllActive(elements);
return;
}
if (!element.classList.contains("active")) {
removeAllActive(elements);
}
element.classList.toggle("active");
};
const factorLimitVar = document.querySelector("#factor-limit");
const siblingIndex1Var = document.querySelector("#--si1");
const siblingIndex2Var = document.querySelector("#--si2");
const currentIndex = document.querySelector("#current");
const calculateSiblingIndex = () => {
const calculationResult =
parseInt(factorLimitVar.innerText) * parseInt(siblingIndex2Var.innerText) +
parseInt(siblingIndex1Var.innerText);
const elements = document.querySelectorAll(".container li");
if (calculationResult === 0) {
removeAllActive(elements);
currentIndex.innerText = 0;
return;
}
changeActive(elements, elements[calculationResult - 1]);
currentIndex.innerText = calculationResult;
};
const setClickListeners = () => {
const allSnippetsSiblingIndex1 = document.querySelectorAll(
"#snippets-si1 pre"
);
const allSnippetsSiblingIndex2 = document.querySelectorAll(
"#snippets-si2 pre"
);
const allSnippets = document.querySelectorAll(".sibling-index pre");
for (let i = 0; i < allSnippets.length / 2; i++) {
allSnippetsSiblingIndex1[i].addEventListener("click", (event) => {
let snippetSiblingIndexValue = parseInt(event.target.attributes[1].value);
siblingIndex1Var.innerText = allSnippetsSiblingIndex1[
i
].classList.contains("active")
? 0
: snippetSiblingIndexValue;
changeActive(allSnippetsSiblingIndex1, allSnippetsSiblingIndex1[i]);
calculateSiblingIndex();
});
allSnippetsSiblingIndex2[i].addEventListener("click", (event) => {
let snippetSiblingIndexValue = parseInt(event.target.attributes[1].value);
siblingIndex2Var.innerText = allSnippetsSiblingIndex2[
i
].classList.contains("active")
? 0
: snippetSiblingIndexValue;
changeActive(allSnippetsSiblingIndex2, allSnippetsSiblingIndex2[i]);
calculateSiblingIndex();
});
}
};
const setListenerForSnippets = () => {
const factorLimit = parseInt(factorLimitVar.innerText);
let allSnippets = document.querySelectorAll(".sibling-index pre");
let allElements = selectElements(factorLimit);
for (let i = 0; i < allSnippets.length; i++) {
allSnippets[i].addEventListener("mouseover", (event) => {
toggleSelectedElements(allElements[i]);
});
allSnippets[i].addEventListener("mouseout", (event) => {
toggleSelectedElements(allElements[i]);
});
}
};
setClickListeners();
setListenerForSnippets();
/* Adding and Removing Elements */
const elements = document.querySelector(".container");
const elementsInput = document.querySelector("#elements");
const elementsLabelValue = document.querySelector(
"label[for='elements'] .value"
);
const createLiElement = (index) => {
const newElement = document.createElement("li");
newElement.innerText = index;
return newElement;
};
elementsInput.addEventListener("input", (event) => {
const targetElementsNumber = parseInt(event.target.value);
elementsLabelValue.innerHTML = targetElementsNumber;
elements.innerHTML = "";
for (let i = 1; i <= targetElementsNumber; i++) {
const newElement = createLiElement(i);
elements.appendChild(newElement);
}
setListenerForSnippets();
calculateSiblingIndex();
});
/* Changing Factor and Snippets */
const createSnippetElement = (siblingIndex, value) => {
const newCodeElement = document.createElement("code");
const newPreElement = document.createElement("pre");
newPreElement.setAttribute("data-sibling-index", siblingIndex);
newPreElement.setAttribute("data-value", value);
const factorLimit = parseInt(factorLimitVar.innerText);
if (siblingIndex === 1) {
newPreElement.innerText = `li:nth-child(${factorLimit}n + ${value}){--si1: ${value};}`;
}
if (siblingIndex === 2) {
newPreElement.innerText = `li:nth-child(n + ${
factorLimit * value
}):nth-child(-n + ${factorLimit * (value + 1) - 1}){--si2: ${value};}`;
}
newCodeElement.appendChild(newPreElement);
return newCodeElement;
};
const snippetsSiblingIndex1 = document.querySelector("#snippets-si1");
const snippetsSiblingIndex2 = document.querySelector("#snippets-si2");
const factorInput = document.querySelector("#factor");
const factorLabelValue = document.querySelector("label[for='factor'] .value");
const currentMax = document.querySelector("#max");
factorInput.addEventListener("input", (event) => {
const targetFactor = parseInt(event.target.value);
factorLabelValue.innerText = targetFactor;
factorLimitVar.innerText = targetFactor;
snippetsSiblingIndex1.innerHTML = "<h2>--si1:</h2>";
snippetsSiblingIndex2.innerHTML = "<h2>--si2:</h2>";
const maxIndexPossible = targetFactor ** 2 - 1;
currentMax.innerText = `(${maxIndexPossible} max)`;
for (let i = 1; i < targetFactor; i++) {
const newSnippetSiblingIndex1 = createSnippetElement(1, i);
const newSnippetSiblingIndex2 = createSnippetElement(2, i);
snippetsSiblingIndex1.appendChild(newSnippetSiblingIndex1);
snippetsSiblingIndex2.appendChild(newSnippetSiblingIndex2);
}
setClickListeners();
setListenerForSnippets();
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.