Fresheyeball
Fresheyeball

Reputation: 30015

sort elements in jQuery object

<div class="myClass">1</div>
<div class="myClass">2</div>
<div class="myClass">3</div>
<div class="myClass">4</div>
<div class="myClass">5</div>
<div class="myClass">6</div>

var $myClass = $('.myClass');
$myClass.eq(0).text() //returns '1'
$myClass.eq(4).text() //returns '5'
$myClass.eq(5).text() //returns '6'

What I want to do is reorder the objects in jQuery manually.

//fantasy command reversing
$myClass.eqSorter([5,4,3,2,1,0]);
$myClass.eq(0).text() //returns '6'
$myClass.eq(5).text() //returns '1'

What I really want is to allow a fully custom order input

//fantasy command custom ordering
$myClass.eqSorter([3,5,4,2,0,1]);
$myClass.eq(0).text() //returns '4'
$myClass.eq(5).text() //returns '2'

Now I've looked at basic ways of doing this like .sort as a part of javascript, but .sort takes a dynamic argument the compares things, and does not allow for a fully custom order. I also looked at making a new blank jquery object that I could pass elements into like this $newObjectVar.eq(0) = $myClass.eq(3); for example. But as of yet I have not found a way to make a blank jQuery object like this.

Upvotes: 1

Views: 3196

Answers (1)

jfriend00
jfriend00

Reputation: 707326

You could make a custom method like this that would reorder the existing DOM elements in the jQuery object. One issue is that many operations on jQuery objects sort them into DOM order so they may not stay in this order if you carry out other operations on the jQuery object.

jQuery.fn.eqSorter = function(array) {
    // get copy of DOM element array
    var copy = this.toArray();
    // don't exceed bounds of arrays
    var len = Math.min(array.length, copy.length);
    for (var i = 0; i < len; i++) {
        var index = array[i];
        // make sure incoming index is within bounds of DOM object array
        if (index < copy.length) {         
            // put the index item into the i location
            this[i] = copy[index];
        }
    }
    return this;
}

$myClass.eqSorter([5,4,3,2,1,0]);

After some more thinking, here's another way of doing things that is also a lot more robust as it prevents dups and holes even if the array passed in has errors in it. The result will be only the elements that match the indexes in the passed in array. Any dups or indexes out of range will be ignored.

The jQuery way of doing things is generally not to change the DOM elements in a given jQuery object, but to modify the existing jQuery object into a new jQuery object. To implement it that way, with the added robustness you could do this:

jQuery.fn.eqSorter = function(array) {
    var elems = [], doneIndexes = {}, index;
    for (var i = 0, len = array.length; i < len; i++) {
        index = array[i];
        // if the index is not out of range and it's not a dup, 
        // then get the corresponding DOM element from the jQuery object
        // and put it into the new array
        if (!doneIndexes[index] && index < this.length && index >= 0) {
            elems.push(this[index]);
            doneIndexes[index] = true;
        }
    }
    return this.pushStack(elems, "eqSorter", arguments);
}

var $myOrderedObj = $myClass.eqSorter([5,4,3,2,1,0]);

This has the following behavior:

  • Any duplicate indexes in the passed in array are ignored (only the first one is processed)
  • Any indexes that are out of range are ignored
  • Any DOM elements in the initial jQuery object that are not referenced by an index will not appear in the result
  • This method returns a new jQuery object and can be used with .end()

There's a working testbed here: http://jsfiddle.net/jfriend00/DPhc9/

Upvotes: 4

Related Questions