4
<div class="items">

<div class="item" id="item1">
    <h4>name</h4>
    <img src="">
    <p class="price"></p>
</div>

<div class="item" id="item2">
    <h4>name</h4>
    <img src="">
    <p class="price"></p>
</div>

<div class="item" id="item4">
    <h4>name</h4>
    <img src="">
    <p class="price"></p>
</div>

What would be the simpliest way, in this layout, to sort items by "price" using JS only? Say, there is webshop with items and user wants to sort items by price ascending

1 Answers1

6

Use Array#sort method with custom sort function.

// get the aprent element
var parent = document.querySelector('.items');

// get all children element and convert into array
// for newer browser you can use `Array.from` instead
// of `[].slice.call()`
[].slice.call(parent.children)
  // sort them using custom sort function
  .sort(function(a, b) {
    // get text content in .price and return difference
    return getPrice(a) - getPrice(b);
    // iterate and append again in new sorted order
  }).forEach(function(ele) {
    parent.appendChild(ele);
  })

// function for getting the price value from the element
function getPrice(ele) {
  // parse the string
  return Number(ele
      // get the price element
      .querySelector('.price')
      // get text content
      .textContent
      // replace all char except digit and dot
      .replace(/[^\d.]+/g, ''))
      // or instead of replace use match method to 
      // get price value
      // .match(/\d*(?:\.\d+)?/)[0]
    // return 0 if NaN
    || 0;
}
<div class="items">

  <div class="item" id="item1">
    <h4>name</h4>
    <img src="">
    <p class="price">1</p>
  </div>

  <div class="item" id="item2">
    <h4>name</h4>
    <img src="">
    <p class="price">23</p>
  </div>

  <div class="item" id="item4">
    <h4>name</h4>
    <img src="">
    <p class="price">3</p>
  </div>
</div>

UPDATE : For changing order based on a dropdown do something like this using a listener.

var parent = document.querySelector('.items'),
  sel = document.querySelector('#order');

function sortElement() {
  [].slice.call(parent.children)
    .sort(function(a, b) {
      // change return value based on order
      return sel.value * (getPrice(a) - getPrice(b));
    }).forEach(function(ele) {
      parent.appendChild(ele);
    })
}

function getPrice(ele) {
  return Number(ele
      .querySelector('.price')
      .textContent
      .replace(/[^\d.]+/g, ''))
    // .match(/\d*(?:\.\d+)?/)[0]
    || 0;
}

// initially sort the element
sortElement();
// bind click event handler
sel.addEventListener('change', sortElement);
<select id="order">
  <option value="1" selected>a-b</option>
  <option value="-1">b-a</option>
</select>
<div class="items">

  <div class="item" id="item1">
    <h4>name</h4>
    <img src="">
    <p class="price">1</p>
  </div>

  <div class="item" id="item2">
    <h4>name</h4>
    <img src="">
    <p class="price">23</p>
  </div>

  <div class="item" id="item4">
    <h4>name</h4>
    <img src="">
    <p class="price">3</p>
  </div>
</div>
Pranav C Balan
  • 113,687
  • 23
  • 165
  • 188
  • 1
    To avoid repetition it would be good to see the "evaluator" that calculates either side of the `a - b` term into a separate function – Alnitak Jan 03 '17 at 08:39
  • Probably you should consider parsing to only numeric value. `.replace(/[^0-9]/g,'')`. Price will be shown in currency format so its more likely to break sorting – Rajesh Jan 03 '17 at 08:39
  • @Rajesh : thanks, updated the answer – Pranav C Balan Jan 03 '17 at 08:43
  • @PranavCBalan Glad to contribute. I also have a grey question. Its considered a bad practice to manipulate DOM in loop but under such circumstances, what could be the best option? Appending in loop or creating a dummy element and adding sorted list to this element and then remove old div and replace it with new one? – Rajesh Jan 03 '17 at 08:47
  • 2
    @Rajesh citation needed - considered bad why, and by whom? The appending in a loop like this is very effective because the `parent.append(child)` will move the _same_ element (which may have event handlers, etc) to the right place. – Alnitak Jan 03 '17 at 08:51
  • @Alnitak : yup, you are right.... there is nothing is recreating it's just moving the same element one place to another without changing any other property – Pranav C Balan Jan 03 '17 at 08:56
  • Right now script is loading automatically on page load..i would like to use – zoran lazarevic Jan 03 '17 at 14:48
  • @zoranlazarevic : check update – Pranav C Balan Jan 03 '17 at 14:56