最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Sort UL numerically and alphabetically, using each items data-char value - Stack Overflow

programmeradmin0浏览0评论

I'm wanting to sort this UL numerically and alphabetically, using each items data-char value.

Note: I'm only wanting to sort the parent UL, not the child UL elements.

<ul>
  <li data-char="w">
    <span>W</span>
    <ul>
      <li>WWWWWWWWWWWWWW</li>
    </ul>
  </li>
  <li data-char="5">
    <span>5</span>
    <ul>
      <li>55555555555555</li>
    </ul>
  </li>
   <li data-char="a">
    <span>A</span>
    <ul>
      <li>AAAAAAAAAAAAAA</li>
    </ul>
  </li>
    <li data-char="1">
    <span>1</span>
    <ul>
      <li>11111111111</li>
    </ul>
  </li>
</ul>

I'm able to acplish this with jQuery by doing:

function sortCharLi(a, b) {

    var va = a.dataset.char.toString().charCodeAt(0),
        vb = b.dataset.char.toString().charCodeAt(0);

    // add weight if it's a number
    if (va < 'a'.charCodeAt(0)) va += 100;
    if (vb < 'a'.charCodeAt(0)) vb += 100;

    return vb < va ? 1 : -1;
}

$('ul > li').sort(sortCharLi).appendTo('ul');

But I'm needing to remove the jQuery dependency so that's not an option any more.

Any ideas how I may do this without jQuery?

JSBIN

I'm wanting to sort this UL numerically and alphabetically, using each items data-char value.

Note: I'm only wanting to sort the parent UL, not the child UL elements.

<ul>
  <li data-char="w">
    <span>W</span>
    <ul>
      <li>WWWWWWWWWWWWWW</li>
    </ul>
  </li>
  <li data-char="5">
    <span>5</span>
    <ul>
      <li>55555555555555</li>
    </ul>
  </li>
   <li data-char="a">
    <span>A</span>
    <ul>
      <li>AAAAAAAAAAAAAA</li>
    </ul>
  </li>
    <li data-char="1">
    <span>1</span>
    <ul>
      <li>11111111111</li>
    </ul>
  </li>
</ul>

I'm able to acplish this with jQuery by doing:

function sortCharLi(a, b) {

    var va = a.dataset.char.toString().charCodeAt(0),
        vb = b.dataset.char.toString().charCodeAt(0);

    // add weight if it's a number
    if (va < 'a'.charCodeAt(0)) va += 100;
    if (vb < 'a'.charCodeAt(0)) vb += 100;

    return vb < va ? 1 : -1;
}

$('ul > li').sort(sortCharLi).appendTo('ul');

But I'm needing to remove the jQuery dependency so that's not an option any more.

Any ideas how I may do this without jQuery?

JSBIN

Share edited May 30, 2016 at 9:19 Pranav C Balan 115k25 gold badges171 silver badges195 bronze badges asked May 24, 2016 at 20:43 dittoditto 6,32710 gold badges58 silver badges91 bronze badges 0
Add a ment  | 

4 Answers 4

Reset to default 6 +100

You can get the ul using getElemetsByTagName and children li can be get from the element object using children property.

function order(ul) {
  // get html children elements of li
  // in case of ul children will be li
  // ` Array.from` will hell helps to convert them into array
  var elements = Array.from(ul.children);

  // sort them with the same code
  elements.sort(function(a, b) {
    var va = a.getAttribute('data-char').charCodeAt(0),
      vb = b.getAttribute('data-char').charCodeAt(0),
      charCA = 'a'.charCodeAt(0);

    // add weight if it's a number
    if (va < charCA) va += 100;
    if (vb < charCA) vb += 100;
    // just get the difference and return to sort them
    return va - vb;
  });

  // append back to update the order
  // forEach can be used to update since it's in array format
  elements.forEach(function(ele) {
    ul.appendChild(ele)
  });
}


// get ul tag from dom and pass as parameter
// although you can use id selector or querySelector, etc
// it depends up on your need, here you just need to pass the dom reference of `ul` to be sorted
order(document.getElementsByTagName('ul')[0]);
<ul>
  <li data-char="w">
    <span>W</span>
    <ul>
      <li>WWWWWWWWWWWWWW</li>
    </ul>
  </li>
  <li data-char="5">
    <span>5</span>
    <ul>
      <li>55555555555555</li>
    </ul>
  </li>
  <li data-char="a">
    <span>A</span>
    <ul>
      <li>AAAAAAAAAAAAAA</li>
    </ul>
  </li>
  <li data-char="1">
    <span>1</span>
    <ul>
      <li>11111111111</li>
    </ul>
  </li>
</ul>


UPDATE : If you want to use them in chain as in jQuery, then extend the prototype

NodeList.prototype.sortElements = function(custom) {
  // if custom sort function passed then sort based on that
  if (typeof custom === 'function')
    return [].slice.call(this).sort(custom);
  // otherwise apply sort method directly
  return [].slice.call(this).sort();
  // you can also use Array.from(this), which only works in latest browsers  
}

Array.prototype.updateOrder = function() {
  // iterate over array element
  this.forEach(function(ele) {
    // append to the parent element
    ele.parentNode.appendChild(ele);
  })
}

// sort them with the same code
function sortFn(a, b) {
  var va = a.getAttribute('data-char').charCodeAt(0),
    vb = b.getAttribute('data-char').charCodeAt(0),
    charCA = 'a'.charCodeAt(0);
  // add weight if it's a number
  if (va < charCA) va += 100;
  if (vb < charCA) vb += 100;
  // just get the difference and return to sort them
  return va - vb;
}


// get li elements which have `data-char` attribute
document.querySelectorAll('ul li[data-char]')
  .sortElements(sortFn) // call sortElements methods we defined with custom sort function
  .updateOrder(); // call updateOrder to update the order of element
<ul>
  <li data-char="w">
    <span>W</span>
    <ul>
      <li>WWWWWWWWWWWWWW</li>
    </ul>
  </li>
  <li data-char="5">
    <span>5</span>
    <ul>
      <li>55555555555555</li>
    </ul>
  </li>
  <li data-char="a">
    <span>A</span>
    <ul>
      <li>AAAAAAAAAAAAAA</li>
    </ul>
  </li>
  <li data-char="1">
    <span>1</span>
    <ul>
      <li>11111111111</li>
    </ul>
  </li>
</ul>

This function preserves your original sorter method. The function expects the ul element to be passed:

function sortThem(ul) {
    var nodes = Array.prototype.slice.call(ul.childNodes).filter(function(el) {
        // Could use QSA with scope depending on browser support here
        return el.tagName === 'LI';
    });

    nodes.sort(function(a, b) {
        var va = a.getAttribute('data-char').charCodeAt(0),
            vb = b.getAttribute('data-char').charCodeAt(0);

        // add weight if it's a number
        if (va < 'a'.charCodeAt(0)) va += 100;
        if (vb < 'a'.charCodeAt(0)) vb += 100;

        return vb < va ? 1 : -1;
    });

    nodes.forEach(function(node) {
        ul.appendChild(node);
    });
}

You may use querySelectorAll for $('ul > li'). Pay attention, the right selector should be $('ul > li[data-char]') because you are inrested only in li tags having the data-char as attribute.

To convert the NodeList returned from querySelectorAll to array you can use Array.from.

To substitute the appendTo you may use forEach.

So the code could be:

function sortCharLi(a, b) {
  var va = a.getAttribute('data-char').charCodeAt(0),
      vb = b.getAttribute('data-char').charCodeAt(0);

  // add weight if it's a number
  if (va < 'a'.charCodeAt(0)) va += 100;
  if (vb < 'a'.charCodeAt(0)) vb += 100;

  return vb < va ? 1 : -1;
}

window.onload = function() {
  // $('ul > li').sort(sortCharLi).appendTo('ul');
  
  Array.from(document.querySelectorAll('ul > li[data-char]')).sort(sortCharLi).forEach(function(element, index) {
    element.parentNode.appendChild(element);
  });
}
<ul>
    <li data-char="w">
        <span>W</span>
        <ul>
            <li>WWWWWWWWWWWWWW</li>
        </ul>
    </li>
    <li data-char="5">
        <span>5</span>
        <ul>
            <li>55555555555555</li>
        </ul>
    </li>
    <li data-char="a">
        <span>A</span>
        <ul>
            <li>AAAAAAAAAAAAAA</li>
        </ul>
    </li>
    <li data-char="1">
        <span>1</span>
        <ul>
            <li>11111111111</li>
        </ul>
    </li>
</ul>

UPDATE A shorter way to convert:

$('ul > li[data-char]').sort(sortCharLi).appendTo('ul');

to pure javaScript can be:

document.querySelectorAll('ul > li[data-char]').sort(sortCharLi).replaceWith();

In order to achieve this result the following methods have to be added:

NodeList.prototype.sort = function(callBack) {
    if (typeof callBack === 'function') {
        return [].slice.call(this).sort(callBack);
    } else {
        return [].slice.call(this).sort();
    }
}

Array.prototype.replaceWith = function() {
    this.forEach(function(element, index) {
        element.parentNode.appendChild(element);
    });
    return this;

}

In this way there exist the possibility to chain methods like in jQuery:

function sortCharLi(a, b) {
  var va = a.getAttribute('data-char').charCodeAt(0),
      vb = b.getAttribute('data-char').charCodeAt(0);

  // add weight if it's a number
  if (va < 'a'.charCodeAt(0)) va += 100;
  if (vb < 'a'.charCodeAt(0)) vb += 100;

  return vb < va ? 1 : -1;
}

NodeList.prototype.sort = function(callBack) {
  if (typeof callBack === 'function') {
    return [].slice.call(this).sort(callBack);
  } else {
    return [].slice.call(this).sort();
  }
}

Array.prototype.replaceWith = function() {
  this.forEach(function(element, index) {
    element.parentNode.appendChild(element);
  });
  return this;
}

window.onload = function () {
  document.querySelectorAll('ul > li[data-char]').sort(sortCharLi).replaceWith();
}
<ul>
    <li data-char="w">
        <span>W</span>
        <ul>
            <li>WWWWWWWWWWWWWW</li>
        </ul>
    </li>
    <li data-char="5">
        <span>5</span>
        <ul>
            <li>55555555555555</li>
        </ul>
    </li>
    <li data-char="a">
        <span>A</span>
        <ul>
            <li>AAAAAAAAAAAAAA</li>
        </ul>
    </li>
    <li data-char="1">
        <span>1</span>
        <ul>
            <li>11111111111</li>
        </ul>
    </li>
</ul>

This is the best answer I can snap off from work, but it might need to be tailored to your html structure.

FUNCTION TO SORT UL WITHOUT JQUERY:

function sortUnorderedList(ul, sortDescending) {
 if(typeof ul == "string")
    ul = document.getElementById(ul);

  // Idiot-proof, remove if you want
  if(!ul) {
    alert("The UL object is null!");
    return;
  }

  // Get the list items and setup an array for sorting
  var lis = ul.getElementsByTagName("LI");
  var vals = [];

 // Populate the array
  for(var i = 0, l = lis.length; i < l; i++)
    vals.push(lis[i].innerHTML);

  // Sort it
  vals.sort();

  // Sometimes you gotta DESC
  if(sortDescending)
    vals.reverse();

 // Change the list on the page
  for(var i = 0, l = lis.length; i < l; i++)
    lis[i].innerHTML = vals[i];
}

USAGE:

sortUnorderedList("ID_OF_LIST");
发布评论

评论列表(0)

  1. 暂无评论