4
\$\begingroup\$

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>

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

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

\$\endgroup\$
3
  • \$\begingroup\$ Great piece of code. But mine is... slimmer. Why should I use yours? \$\endgroup\$ Commented 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
    Commented Jan 11, 2019 at 12:43
  • 1
    \$\begingroup\$ Find the original question, with my answer and many other, HERE. \$\endgroup\$ Commented Jan 11, 2019 at 13:25
2
\$\begingroup\$

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. Generally document.getElementsByClassName will be quicker than document.querySelectorAll (see this post for more information) and the former also returns a live HTMLCollection so it wouldn't need to be queried each time.
  • only setting the order style on the items once, since that never changes between calls to reverseBoxes()
  • 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>

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.