11

I have a list of elements that I get using JQuery.

var $self = $(this);
var $fields = $('.identifier-controls', $self);

This list is the list of elements that need to be manipulated in some way when the control renders. Each element in the list of $fields contains a data attribute called "data-order". This attribute tells me what order I should arrange the elements with in the control (ah requirements). The order does not have to be in direct linear order (meaning that the first control can have an attribute value of 10 and the next one 15 and the next one 17 etc. They just need to appear in the asc order. Is there a simple way to achieve this? All the ways that I can come up with seem a bit over complicated.

The Sheek Geek
  • 4,126
  • 6
  • 38
  • 48

4 Answers4

11

Steps to sort jquery "array-like" objects by data attribute...

  1. select all object via jquery selector
  2. convert to actual array (not array-like jquery object)
  3. sort the array of objects
  4. convert back to jquery object with the array of dom objects

Html

<div class="item" data-order="2">2</div>
<div class="item" data-order="1">1</div>
<div class="item" data-order="4">4</div>
<div class="item" data-order="3">3</div>

Plain jquery selector

$('.item');
[<div class="item" data-order="2">2</div>,
 <div class="item" data-order="1">1</div>,
 <div class="item" data-order="4">4</div>,
 <div class="item" data-order="3">3</div>
]

Lets sort this by data-order

function getSorted(selector, attrName) {
    return $($(selector).toArray().sort(function(a, b){
        var aVal = parseInt(a.getAttribute(attrName)),
            bVal = parseInt(b.getAttribute(attrName));
        return aVal - bVal;
    }));
}
> getSorted('.item', 'data-order')
[<div class="item" data-order="1">1</div>,
 <div class="item" data-order="2">2</div>,
 <div class="item" data-order="3">3</div>,
 <div class="item" data-order="4">4</div>
]

See how getSorted() works.

Hope this helps!

Troy Grosfield
  • 2,133
  • 20
  • 19
8

Try this:

$(function(){
   var $self = $(this);
   var sortedList = $('.identifier-controls', $self).sort(function(lhs, rhs){
      return parseInt($(lhs).attr("data-order"),10) - parseInt($(rhs).attr("data-order"),10);
   });
});

the variable sortedList now has the sorted elements.

epignosisx
  • 6,152
  • 2
  • 30
  • 31
  • @TheSheekGeek: See my note on one of the other answers, as long as you're aware you're relying on undocumented behavior that can change at any time, and you're mucking about with the order of jQuery's stored elements, which is going to have knock-on effects as many jQuery methods guarantee things will be done in a specific order. (I'm also not seeing how this moves things in the DOM; was that not a requirement?) – T.J. Crowder Dec 08 '11 at 17:22
  • There is code in another part of the control that handles adding them to the DOM. What the control actually does is take a bunch of textboxes and labels that already exist (just a list of li elements) and steal them, adding them back to the DOM to make a control that looks like a dropdown with all the textboxes in them. The joy of requirements. (Its actually not that bad) – The Sheek Geek Dec 09 '11 at 15:45
7

Something like:

$fields.sort(function(a, b) {
    return a.getAttribute('data-order') > b.getAttribute('data-order');
}).appendTo($fields.parent());

Fiddle: http://jsfiddle.net/gCFzc/

David Hellsing
  • 106,495
  • 44
  • 176
  • 212
  • 1
    jQuery objects are **not** arrays. They are array-like. The `sort` function of jQuery objects is undocumented and, if you look at the jQuery.js file, they say it's for internal use only. So a caveat is in order. That said, if you're sure that jQuery doesn't get messed up if you mess up the order of its elements (I'm not), you can manually apply `Array.prototype.sort` to it; [the spec](http://es5.github.com/#x15.4.4.11) is quite clear that you should be able to use it on array-like non-array objects. Mind you, once you've sorted it, they still show in the same place in the DOM. – T.J. Crowder Dec 08 '11 at 16:24
  • @T.J.Crowder bad design decision on part of jQuery devs to call it sort and make selector result array-like. Many duck-typing js coders would fall into this trap. – soulcheck Dec 08 '11 at 17:19
  • it doesn't work anymore – Martin Zvarík Aug 21 '23 at 15:38
3

An insertion sort would be a fairly straight-forward way of doing it.

Or being a bit subversive, you can take advantage of the JavaScript engine for this:

var $fields, $container, sorted, index;

$container = $('body');
$fields = $("div[data-order]", $container);
sorted = [];
$fields.detach().each(function() {
    sorted[parseInt(this.getAttribute("data-order"), 10)] = this;
});
sorted.forEach(function(element) {
    $container.append(element);
});

Live Example:

(function($) {
    var $fields, $container, sorted, index;
  
    $container = $('body');
    $fields = $("div[data-order]", $container);
    sorted = [];
    $fields.detach().each(function() {
        sorted[parseInt(this.getAttribute("data-order"), 10)] = this;
    });
    sorted.forEach(function(element) {
        $container.append(element);
    });
})(jQuery);
<div data-order="30">30</div>
<div data-order="40">40</div>
<div data-order="10">10</div>
<div data-order="20">20</div>
<div data-order="1">1</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

Here's how that works:

  • We get the container, and get the fields from it.

  • We create a blank array. Remember that JavaScript arrays aren't really arrays at all, and they're inherently sparse.

  • We detach the fields, then loop through them, getting the data-order from the DOM element and adding the DOM element to the array in that position. This assumes that data-order values are unique.

  • Once we have the array of raw DOM elements, we loop through it using forEach, appending them to the container. I didn't use jQuery.each because jQuery.each will call the callback even for non-existant array indexes, which if your data-order values are quite sparse could be a problem. forEach iterates the entries in numeric order, skipping over ones that don't exist.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875