Recently I found a question on StackOverflow that seemed very interesting: How to make HTML elements "zig zag" this way:
-----------------
|A > B > C > D > E|
|J < I < H < G < F|
-----------------
---
|A H|
|B G|
|C F|
|D E|
---
I was able to implement a simple solution for this problem with Flexbox and a bit of JavaScript (works for any even number of elements):
var reverseBoxes = function () {
var flexItems = document.querySelectorAll(".child"),
flexItemsCount = flexItems.length,
reverseAt = flexItems.length / 2,
breakPoint = 480;
for (var i = reverseAt; i < flexItemsCount; i++) {
flexItems[i].style.order = flexItemsCount - i;
}
for (var j = 0; j < flexItemsCount; j++) {
if (window.innerWidth > breakPoint) {
flexItems[j].style.width = (100 / flexItemsCount) * 2 - 2 + "%";
flexItems[j].style.height = "auto";
} else {
flexItems[j].style.height = (100 / flexItemsCount) * 2 - 2 + "%";
flexItems[j].style.width = "auto";
}
}
}
reverseBoxes();
window.addEventListener("resize", reverseBoxes);
body {
font-family: Arial, sans-serif;
font-size: 18px;
margin: 0;
padding: 0;
}
.parent {
display: flex;
flex-wrap: wrap;
list-style-type: none;
padding: 0;
height: 100vh;
}
.child {
margin: 1%;
text-align: center;
background: #069;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
@media only screen and (max-width: 480px) {
.parent {
flex-direction: column;
}
.child {
width: 48%;
}
}
<div class="parent">
<div class="child">A</div>
<div class="child">B</div>
<div class="child">C</div>
<div class="child">D</div>
<div class="child">E</div>
<div class="child">F</div>
<div class="child">G</div>
<div class="child">H</div>
<div class="child">I</div>
<div class="child">J</div>
</div>
2 Answers 2
Tiny adjustment : you can update all your objects in your first loop and get rid of the second.
var reverseBoxes = function() {
var flexItems = document.querySelectorAll(".child"),
flexItemsCount = flexItems.length,
reverseAt = flexItems.length / 2,
breakPoint = 480;
if (window.innerWidth > breakPoint) {
for (var i = reverseAt; i < flexItemsCount; i++) {
flexItems[i].style.order = flexItemsCount - i;
// First half of items
flexItems[flexItemsCount - i - 1].style.width = (100 / flexItemsCount) * 2 - 2 + "%";
flexItems[flexItemsCount - i - 1].style.height = "auto";
// Second half of items
flexItems[i].style.width = (100 / flexItemsCount) * 2 - 2 + "%";
flexItems[i].style.height = "auto";
}
} else {
for (var i = reverseAt; i < flexItemsCount; i++) {
flexItems[i].style.order = flexItemsCount - i;
// First half of items
flexItems[flexItemsCount - i - 1].style.height = (100 / flexItemsCount) * 2 - 2 + "%";
flexItems[flexItemsCount - i - 1].style.width = "auto";
// Second half of items
flexItems[i].style.height = (100 / flexItemsCount) * 2 - 2 + "%";
flexItems[i].style.width = "auto";
}
}
}
Edit : got the if out of the for loop
-
\$\begingroup\$ Great piece of code. But mine is... slimmer. Why should I use yours? \$\endgroup\$Razvan Zamfir– Razvan Zamfir2019年01月11日 12:29:05 +00:00Commented Jan 11, 2019 at 12:29
-
\$\begingroup\$ Actually if you remove the comments it is the same amount of lines. If I let the comments I can give it a small diet of one line by getting the if out of the for loop. My bad I forgot a line. There is no diet by getting the if out. \$\endgroup\$Aweuzegaga– Aweuzegaga2019年01月11日 12:43:24 +00:00Commented Jan 11, 2019 at 12:43
-
1\$\begingroup\$ Find the original question, with my answer and many other, HERE . \$\endgroup\$Razvan Zamfir– Razvan Zamfir2019年01月11日 13:25:21 +00:00Commented Jan 11, 2019 at 13:25
This code could be optimized by:
- selecting child elements by class name with
document.getElementsByClassName()and only do this once instead of each time the function runs. Generallydocument.getElementsByClassNamewill be quicker thandocument.querySelectorAll(see this post for more information) and the former also returns a liveHTMLCollectionso it wouldn't need to be queried each time. - only setting the
orderstyle on the items once, since that never changes between calls toreverseBoxes() - calculate the percentage height or width once instead of in each iteration of looping through the elements
See this demonstrated in the updated code below. The code to set the order style will only run when those styles are not yet set so those won't get updated each time the function runs. That functionality could also be run when the page loads.
(function() { //IIFE to keep scope of vars limited
var flexItems = document.getElementsByClassName("child"),
flexItemsCount = flexItems.length,
reverseAt = flexItems.length / 2,
breakPoint = 480;
var reverseBoxes = function() {
let height = (100 / flexItemsCount) * 2 - 2 + "%";
let width = "auto";
let i = 0;
if (window.innerWidth > breakPoint) {
width = height; //use value calculated above
height = "auto"; //then set this to "auto"
}
for (const item of flexItems) {
item.style.width = width;
item.style.height = height;
if (i++ >= reverseAt && !item.style.order) {
item.style.order = flexItemsCount - i;
}
}
}
window.addEventListener("resize", reverseBoxes);
document.addEventListener("DOMContentLoaded", reverseBoxes);
})();
body {
font-family: Arial, sans-serif;
font-size: 18px;
margin: 0;
padding: 0;
}
.parent {
display: flex;
flex-wrap: wrap;
list-style-type: none;
padding: 0;
height: 100vh;
}
.child {
margin: 1%;
text-align: center;
background: #069;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
@media only screen and (max-width: 480px) {
.parent {
flex-direction: column;
}
.child {
width: 48%;
}
}
<div class="parent">
<div class="child">A</div>
<div class="child">B</div>
<div class="child">C</div>
<div class="child">D</div>
<div class="child">E</div>
<div class="child">F</div>
<div class="child">G</div>
<div class="child">H</div>
<div class="child">I</div>
<div class="child">J</div>
</div>
You must log in to answer this question.
Explore related questions
See similar questions with these tags.