Cream Whipped Airplane Cream Whipped Airplane - 5 months ago 16
jQuery Question

Sort DIVs based on second class and then sort alphabetically per class based on inner text

EDIT: Solved partially with the solution below. But for some reason, the solution below would either only correctly sort everything alphabetically based on the label, or sort it based on the category, but combination of both didn't work in my case. So instead I used the first part, that sorts everything alphabetically, and then used a for loop to manipulate per category like this:

for (i = 1; i <= 8; i++) { // 8 because I have 8 categories..
$('.ai-category-' + i).each(function(){
$(this).detach().appendTo('#items');
});
}


I'm trying to sort DIVs using class name to first order items per category and then order them alphabetically within each category. But I have too many classnames and I can't seem to figure out how to do this. I will confess that this is above my skillset and I just don't know how to do this at all. I know it would require putting few functions together, but I just don't know where to start.

Currently my list looks like this

<div id="items">
<div class="item ai-category-1 someOtherClass anotherClass">
<div class="info">
<p class="label">ZZ Item Name</p>
</div>
</div>
<div class="item ai-category-2 someOtherClass anotherClass">
<div class="info">
<p class="label">ZZ Item Name</p>
</div>
</div>
<div class="item ai-category-1 someOtherClass randomClass">
<div class="info">
<p class="label">AA Item Name</p>
</div>
</div>
<div class="item ai-category-2 someOtherClass anotherClass">
<div class="info">
<p class="label">AA Item Name</p>
</div>
</div>
</div>


And I need it to order all
.item
based on the second class that says what category it is and then order it using the
.label
alphabetically in that category. So the result should look like this

<div id="items">
<div class="item ai-category-1 someOtherClass randomClass">
<div class="info">
<p class="label">AA Item Name</p>
</div>
</div>
<div class="item ai-category-1 someOtherClass anotherClass">
<div class="info">
<p class="label">ZZ Item Name</p>
</div>
</div>
<div class="item ai-category-2 someOtherClass anotherClass">
<div class="info">
<p class="label">AA Item Name</p>
</div>
</div>
<div class="item ai-category-2 someOtherClass anotherClass">
<div class="info">
<p class="label">ZZ Item Name</p>
</div>
</div>
</div>


There wont always be 4 classes, but there will always be two and the
.item
will always be first and
[class^="ai-category-"]
will always be second. If anyone can recommend any functions that I should look at or offer a solution I will be really thankful.

Answer

There is a jQuery method .sortElemnts() implemented by James. Below code is copied from his blog.

/**
 * jQuery.fn.sortElements
 * --------------
 * @param Function comparator:
 *   Exactly the same behaviour as [1,2,3].sort(comparator)
 *   
 * @param Function getSortable
 *   A function that should return the element that is
 *   to be sorted. The comparator will run on the
 *   current collection, but you may want the actual
 *   resulting sort to occur on a parent or another
 *   associated element.
 *   
 *   E.g. $('td').sortElements(comparator, function(){
 *      return this.parentNode; 
 *   })
 *   
 *   The <td>'s parent (<tr>) will be sorted instead
 *   of the <td> itself.
 */
jQuery.fn.sortElements = (function(){

    var sort = [].sort;

    return function(comparator, getSortable) {

        getSortable = getSortable || function(){return this;};

        var placements = this.map(function(){

            var sortElement = getSortable.call(this),
                parentNode = sortElement.parentNode,

                // Since the element itself will change position, we have
                // to have some way of storing its original position in
                // the DOM. The easiest way is to have a 'flag' node:
                nextSibling = parentNode.insertBefore(
                    document.createTextNode(''),
                    sortElement.nextSibling
                );

            return function() {

                if (parentNode === this) {
                    throw new Error(
                        "You can't sort elements if any one is a descendant of another."
                    );
                }

                // Insert before flag:
                parentNode.insertBefore(this, nextSibling);
                // Remove flag:
                parentNode.removeChild(nextSibling);

            };

        });

        return sort.call(this, comparator).each(function(i){
            placements[i].call(getSortable.call(this));
        });

    };

})();

Below is the demo how Jame's method work with your case.

$(document).ready(function() {
  var item = $('.item');
  var btn = $('button');
  
  btn.on('click', function() {
    item
    .sortElements(function(a, b){
      return $(a).text() > $(b).text() ? 1 : -1;
    })
    .sortElements(function(a, b){
      var aCls = getClassWithPrefix(a, 'ai-category-');
      var bCls = getClassWithPrefix(b, 'ai-category-');
      return aCls > bCls ? 1 : -1;
    });
  });
});

function getClassWithPrefix(ele, prefix) {
  var classes = ele.className.split(/\s+/);
  var clsWithPrefix = undefined;
  classes.forEach(function(cls, i) {
    if(cls.indexOf(prefix) === 0) {
      clsWithPrefix = cls;
    }
  });
  return clsWithPrefix;
}
.ai-category-1:before {
  content: '1:';
  float: left;
}

.ai-category-2:before {
  content: '2:';
  float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/padolsey-archive/jquery.fn/master/sortElements/jquery.sortElements.js"></script>

<div id="items">
  <div class="item ai-category-1 someOtherClass anotherClass">
    <div class="info">
      <p class="label">ZZ Item Name</p>
    </div>
  </div>
  <div class="item ai-category-2 someOtherClass anotherClass">
    <div class="info">
      <p class="label">ZZ Item Name</p>
    </div>
  </div>
  <div class="item ai-category-2 someOtherClass randomClass">
    <div class="info">
      <p class="label">AA Item Name</p>
    </div>
  </div>
  <div class="item ai-category-1 someOtherClass anotherClass">
    <div class="info">
      <p class="label">AA Item Name</p>
    </div>
  </div>
</div>

<button>Sort</button>

Comments