skip to Main Content

vanilla javascript starter here.
I’m trying to sort some of my elements that I have in a list. After browsing a lot of existing solutions but getting none of them to work I think it’s an issue that I don’t know how to formulate what I’m looking for so maybe someone more experienced can help me out.

So I’m trying to get my list sorted alphabetically and then changed in the htmlcollection.
the items look like this:

      <li class="foodListItem" title = "potato">
        <div class="img">
          <a href="" title="potato">
            <img src = "FoodImages/potato.jpg" alt="Potato">
          </a>
        </div>
        <p class="name">
          <a href="" title="potato"> Potato</a>
        </p>
      </li>
      <li class="foodListItem" title="brocolli">
        <div class="img">
          <a href="" title="brocolli">
            <img src = "FoodImages/brocolli.jpg" alt="brocolli">
          </a>
        </div>
        <p class="name">
          <a href="" title="brocolli"> brocolli</a>
        </p>
      </li>

And the closest I’ve gotten to replacing it has been this approach:

function sortImages()
{
    var foodList= document.getElementsByClassName("foodListItem");
    foodArray = Array.from(foodList);
    foodArray.sort(function (a, b){
        if(a.title < b.title)
        {
            return -1;
        }
        if(a.title > b.title){
            return 1;
        }
        return 0;
    });
    for(var i = 0; i < foodArray.length; i++)
    {
        foodList[i].replaceWith(foodArray[i]);
    }
}

With this approach however I seem to be losing elements whilst replacing them and otherwise I haven’t gotten it to replace correctly using other methods.
Anyone know a better solution?

2

Answers


  1. You can actually benefit from getElementsByXy() methods returning live collections. Then you have to find a way to move the items around. A method which I remember is Node.insertBefore(), which allows kind-of bubble sort (I didn’t fully think through if it’s really a bubble sort):

    function doThing(){
      const inputs=document.getElementsByTagName("li");
      for(let i=0;i<inputs.length-1;i++) {
        const item=inputs[i];
        for(let j=i+1;j<inputs.length;j++){
          const jtem=inputs[j];
          if(item.firstChild.value>jtem.firstChild.value)
            item.parentNode.insertBefore(jtem,item);
        }
      }
    }
    <ul>
    <li><input oninput="doThing()"></li>
    <li><input oninput="doThing()"></li>
    <li><input oninput="doThing()"></li>
    <li><input oninput="doThing()"></li>
    <li><input oninput="doThing()"></li>
    <li><input oninput="doThing()"></li>
    </ul>

    Now as I looked into it, there is Element.insertAdjacentElement() which doesn’t need the parentNode ugliness, and would allow after too (not used here):

    function doThing(){
      const inputs=document.getElementsByTagName("li");
      for(let i=0;i<inputs.length-1;i++) {
        const item=inputs[i];
        for(let j=i+1;j<inputs.length;j++){
          const jtem=inputs[j];
          if(item.firstChild.value>jtem.firstChild.value)
            item.insertAdjacentElement("beforebegin",jtem);
        }
      }
    }
    <ul>
    <li><input oninput="doThing()"></li>
    <li><input oninput="doThing()"></li>
    <li><input oninput="doThing()"></li>
    <li><input oninput="doThing()"></li>
    <li><input oninput="doThing()"></li>
    <li><input oninput="doThing()"></li>
    </ul>
    Login or Signup to reply.
  2. replaceWith() truncates the live list: a given HTML element instance can exist in a document only once, so if you replace x with y, then x is actually moved, it disappears from its original location, and thus the HTMLCollection becomes one element shorter. This way the loop fails around the middle (there may be cases when an element replaces itself), and the original list is approximately halved:

    function sortImages()
    {
        var foodList= document.getElementsByTagName("div");
        foodArray = Array.from(foodList);
        foodArray.sort((a, b)=>a.innerText.localeCompare(b.innerText));
        for(var i = 0; i < foodArray.length; i++)
        {
            foodList[i].replaceWith(foodArray[i]);
        }
    }
    <div>q</div>
    <div>w</div>
    <div>e</div>
    <div>r</div>
    <div>t</div>
    <div>y</div>
    <div>u</div>
    <div>i</div>
    <div>o</div>
    <div>p</div>
    <div>a</div>
    <div>s</div>
    <div>d</div>
    <div>f</div>
    <button onclick="sortImages()">Clicky</button>

    As it’s a live collection, it has no explicit add operation, but technically you can extend the document itself, though I wouldn’t call that intuitive: check if i went past length, and use insertAdjacentElement() when it does:

    function sortImages()
    {
        var foodList= document.getElementsByTagName("div");
        foodArray = Array.from(foodList);
        foodArray.sort((a, b)=>a.innerText.localeCompare(b.innerText));
        for(var i = 0; i < foodArray.length; i++)
        {
            if(i<foodList.length)
              foodList[i].replaceWith(foodArray[i]);
            else
              foodList[i-1].insertAdjacentElement("afterend",foodArray[i]);
        }
    }
    <div>q</div>
    <div>w</div>
    <div>e</div>
    <div>r</div>
    <div>t</div>
    <div>y</div>
    <div>u</div>
    <div>i</div>
    <div>o</div>
    <div>p</div>
    <div>a</div>
    <div>s</div>
    <div>d</div>
    <div>f</div>
    <button onclick="sortImages()">Clicky</button>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search