Reputation: 5711
I have an array (a javascript array, not a jQuery object) of DOM nodes that acts as a queue for a function I have that processes them. This array could potentially contain elements that have parent/child, grandparent/child, etc. relationships. I always want higher-level elements to be processed first. My first inclination was to whip up a quicksort function in javascript, but I knew it would be faster if I could use javascript's native Array.prototype.sort
method.
I tried it this way:
domElements.sort(function (a, b) {
return $(a).find(b).length ? 1 :
$(b).find(a).length ? -1 :
0;
});
But it didn't seem to sort it perfectly. I would still sometimes have child elements before their parents. Why doesn't this work? Is there a way to do this with javascript's native sort?
UPDATE: After surveying the methods in the answers, I wanted to know how they performed. Here are the results. Feel free to tweak and see how the performance is for you.
Upvotes: 2
Views: 650
Reputation: 1074465
(Since you asked me to post it. :-) )
You've said you want the parent elements before the children in the array, but your code
domElements.sort(function (a, b) {
return $(a).find(b).length ? 1 :
$(b).find(a).length ? -1 :
0;
});
...returns 1
if a
is a parent of b
, which will put a
later in the array than b
.
So I'm thinking:
domElements.sort(function (a, b) {
return $(a).find(b).length ? -1 :
$(b).find(a).length ? 1 :
0;
});
But checking if an element is a descendant of another element is so straight-forward, I wonder if you really need the allocate-a-jQuery-object-and-then-call-find:
domElements.sort(function (a, b) {
return isParentOf(a, b) ? -1 :
isParentOf(b, a) ? 1 :
0;
});
function isParentOf(parent, elm) {
while (elm) {
elm = elm.parentNode;
if (elm === parent) {
return true;
}
}
return false;
}
Working example But note timmywil's answer — even though it does a bit of unnecessary work (putting the siblings in order, when all you care about is parent/child), there's a pre-baked jQuery function for that!
Upvotes: 1
Reputation: 104780
Sorting random elements in a document according to their source code order will keep childnodes and siblings in their proper positions.
if(!Array.prototype.indexOf){
Array.prototype.indexOf= function(what, i){
if(typeof i!= 'number') i= 0;
var L= this.length;
while(i< L){
if(this[i]=== what) return i;
++i;
}
return -1;
}
}
function nodeSort(nodesArray, pa){
pa= pa || document;
var original= [], src= pa.getElementsByTagName('*'), L= src.length;
for(var i= 0; i<L; i++){
if(nodesArray.indexOf(src[i])!=-1) original.push(src[i]);
}
return nodesArray.sort(function(a, b){
return original.indexOf(a)- original.indexOf(b);
});
}
Upvotes: 0
Reputation: 300
You can save some time by using jQuery's unique
method: http://api.jquery.com/jQuery.unique/
jQuery.unique(domElements);
Upvotes: 2
Reputation: 5711
It looks like I mixed around the negative sign on the one:
domElements.sort(function (a, b) {
return $(a).find(b).length ? -1 :
$(b).find(a).length ? 1 :
0;
});
Seems to work.
Upvotes: 0