Chris Happy
Chris Happy

Reputation: 7295

Element Order being Messed Up from after Moving Elements Twice

I'm making creating some Javascript code so that the overflowing menu links would move into a separate menu called more.

The first time it is loaded, it works perfectly. However, when it is run again, things start shuffling...

JSfiddle

Snippet here:

var tele = document.getElementById('teleporter'),
  	rec = document.getElementById('receiver');

window.onresize = resize;
resize();

function resize() {
  var rChildren = rec.children,
   	 numW = 0;

  for (var i = 0; i < rChildren.length; i++) {
    var child = rChildren[i];
   	var fragment = document.createDocumentFragment().appendChild(child);
    child.outHTML = '';
    tele.appendChild(fragment);
  }
  
  var teleW = tele.offsetWidth,
     tChildren = tele.children;

  for (var i = 0; i < tChildren.length; i++) {
    var child = tChildren[i];
    numW += child.offsetWidth;

    if (numW > teleW) {
      var fragment = document.createDocumentFragment().appendChild(child);
      child.outHTML = '';
      rec.appendChild(fragment);
      i--;
    }
  }
  
}
#teleporter {
  height: 20px;
  overflow: hidden;
  box-sizing: border-box;
  padding: 0;
}

li {
  float: left;
  padding: 0 10px;
  box-sizing: border-box;
  list-style: none;
}
<ul id="teleporter">
  <li>List item 0</li>
  <li>List item 1</li>
  <li>List item 2</li>
  <li>List item 3</li>
  <li>List item 4</li>
  <li>List item 5</li>
  <li>List item 6</li>
  <li>List item 7</li>
  <li>List item 8</li>
  <li>List item 9</li>
  <li>List item 10</li>
  <li>List item 11</li>
  <li>List item 12</li>
  <li>List item 13</li>
</ul>
<div>More:
  <ul id="receiver"></ul>
</div>

Why isn't it working?

Upvotes: 3

Views: 227

Answers (2)

zer00ne
zer00ne

Reputation: 44000

To ensure that the <li> are always in order, we make each <ul> a flex container and then assign each <li> the CSS property order

Details are commented in example

// Collect all <li> into an array
const items = Array.from(document.querySelectorAll('li'));
// Assign each <li> the CSS property {order: index}
items.forEach((item, index) => item.style.order = index);

// Bind the window to the resize event
window.onresize = resize;
// Call resize() for an initial sizing
resize();

function resize(e) {
  // Reference both <ul>
  const tX = document.querySelector('.tX');
  const rX = document.querySelector('.rX');
  // Get the width of tX
  let tXW = tX.offsetWidth;
  // Getv the width of the first <li>
  let itemW = document.querySelector('li').offsetWidth;
  /* 
  Divide the current width of tX by the width of a <li>
  the result is the number of <li> that can fit in tX
  in a single line
  */
  let tXTotal = Math.floor(tXW / itemW) - 1;

  /*
  First array are the first <li> up to the last <li> that
  can fit inside of tX
  Second array is the remaining <li>
  */
  let tXArray = items.slice(0, tXTotal);
  let rXArray = items.slice(tXTotal);

  // Append the array of <li> to the appropriate <ul>
  tXArray.forEach(item => tX.append(item));
  rXArray.forEach(item => rX.append(item));
}
all {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

main {
  padding: 5vh 15vw 5vw 10vh;
}

ul {
  list-style: none;
  margin-left: -40px;
}

.tX {
  display: flex;
  flex-flow: row nowrap;
  align-items: center;
  max-width: 75vw;
  height: 20px;
  padding-right: 25vw;
}

.rX {
  display: flex;
  flex-flow: row wrap;
  align-content: center;
}

li {
  min-width: max-content;
  margin: 0 8px 8px;
}
<main>
  <ul class="tX">
    <li>List item 00</li>
    <li>List item 01</li>
    <li>List item 02</li>
    <li>List item 03</li>
    <li>List item 04</li>
    <li>List item 05</li>
    <li>List item 06</li>
    <li>List item 07</li>
    <li>List item 08</li>
    <li>List item 09</li>
    <li>List item 10</li>
    <li>List item 11</li>
    <li>List item 12</li>
    <li>List item 13</li>
  </ul>
  <div>More:
    <ul class='rX'></ul>
  </div>
</main>

Upvotes: 0

Santiago Rebella
Santiago Rebella

Reputation: 2449

I rewrote your resize function to avoid for loops, most probably error is in the hoisting of your i's, and unless you are doing it for a specific reason you can avoid the fragment variable, find below the modified resize, works

      function resize() {
        const rChildren = rec.children;
        let numW = 0;

        [...rChildren].forEach(item => {
          item.outHTML = '';
          tele.appendChild(item);
        })  

        const teleW = tele.offsetWidth,
          tChildren = tele.children;

        [...tChildren].forEach(item => {
          numW += item.offsetWidth;

          if (numW > teleW) {
            item.outHTML = '';
            rec.appendChild(item);
          }
        });
      }

updated fiddle: https://jsfiddle.net/e34p0t6w/3/

Upvotes: 1

Related Questions