There are quite a few table-sorting functions/plugins out there, but I’ve yet to find something sufficiently low-level for general element-sorting. What I wanted was a simple jQuery plugin that would take a sorting function (just like Array.prototype.sort
) as an argument and sort the DOM elements in-place, and would handle situations where the elements didn’t all have the same parent.
To be honest, I didn’t search for very long — I prefer making this kind of stuff myself anyway — so, I present what I think is one of the simplest ways possible to implement a reliable element-sorting function.
UPDATE: Name changed from sort
to sortElements
as per David’s comment.
/** * 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)); }); }; })(); |
Features:
- It’s low-level — you have control over how it sorts (see the “comparator” argument).
- It lets you sort any arbitrary DOM nodes, as long as none of those nodes are within another one of the nodes to be sorted.
- It lets you specify what elements will actually be moved (see the “getSortable” argument).
Usage:
Assuming the following markup:
<ul> <li>Banana</li> <li>Carrot</li> <li>Apple</li> </ul> |
You could sort the items alphabetically like so:
$('li').sortElements(function(a, b){ return $(a).text() > $(b).text() ? 1 : -1; }); |
That would result in:
<ul> <li>Apple</li> <li>Banana</li> <li>Carrot</li> </ul> |
jQuery.fn.sortElements
on Github
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
Great to have your blog back. Did your Twitter account move somewhere? I hope you are ok. Later!
Thanks a lot, i didn’t expected that it was that simple and always relied on plugins for that kind of stuff.
Have you really tried to sort a TABLE with that ? Because I don’t see how it could work to store, for example, a column of a table while keeping rows consistent…
@naholyr, did you see the optional ‘getSortable’ argument? Have a look at the demo and you’ll see that the table-sorting works correctly.
Thanks for this tip!
This is a nice & useful plugin
@James:
The only thing I might change would be to have the comparator argument default to a generic sort algorithm and maybe to even allow a string for some built-in handy sort goodness:
It would add a little to the code, but could save you from a lot of re-implementation of the same sorting algorithms (since the odds are most implementations are going to come down to the same core algorithms.)
never thought it can work this way. really interesting. thanks
I have tested your plug-in and it seems to work pretty well. However, I cannot understand when the second return statement will execute, perhaps it is due to my lack of deep JS understanding. I thought the selfinvoking function would return so the sort.call method would be never executed.
Thanks for this, I was just about to roll my own
I have an eeror i Chrome 6 in line “parentNode.insertBefore(this, nextSibling);”. The console says: Uncaught Error: HIERARCHY_REQUEST_ERR: DOM Exception 3. Could you fix this?
This was very cool, great idea. I simplified it somewhat to avoid the map entirely. Take a look, let me know what you think:
@Avi, I can see what you’ve done there, and it will work in situations where all elements have the same parent, but my intention with
jQuery.fn.sort
was to make sure that it would work regardless of where the sorted elements are in the DOM. See this: http://jsfiddle.net/kbgwB/Hi all,
First thanks for the plugin. I have been using sucessfuly until I found a very dangerous side effect. Here is the problem:
If you have load this plugin in the page, and that’s you are using jQuery multiple selectors like $(‘.classA, .classB, #id1’). Then the matched element get randomly/duplicate sort without calling the sort function, just by using the jQuery selector. And to be more tricky this happens only on IE!!!
I have been trying to debug, and it seems that in jQuery 1.4.2, there is an internal sort function that is call when using multiple selector on IE.
I fix this by renaming the plugin to sortDomElement() and it’s ok.
Hope this can help anybody that will facing the same issue…
@David, I completely overlooked that — thank you for bringing it up! I’ve updated the plugin’s name to “sortElements”.
Hey,
I have run into a problem where the sorting is case sensitive (all elements with capitals come first and all lowercase come after). this mucks things up if your trying to sort things like GPS, Laptop, iphone, Blackberry… any ideas?
@Silas, try this:
Worked like a charm. Thanks!
I’m needing to sort a list of of work based on date, name, or type. The markup that would be used is below.
<ul>
<li>
<h1>Project Name</h1>
<h2>2010</h2>
<span class=”type”>Commercial</span>
</li>
<li>
<h1>Another Project Name</h1>
<h2>2009</h2>
<span class=”type”>Industrial</span>
</li>
</ul>
How would I go about this?
Nevermind, I fixed it. I’m sure my code is a little haggard, but it looks like this:
I’d also to like to note that I’m using jQuery Masonry plug-in and the sorting will not work when Masonry is active. Suggestions?
Thanks! This was a great help! I know I have MAAAAAAANY options, but I like to keep things simple and concise. I was about to create my own, but I am very happy with what you put together!
Great solution, I will be using it in an iPhone Web Development I am making. Thanks a lot, simple and easy :9
Does this work with jquery 1.3.2? I’m not getting any type of sort.
Hello :-
Is it possible to sort in this scenario (code below ).
The comparator is
div.thumbnail div h3 a.text()
and the element to return/ sort is :-div.thumbnail div
I have tried a few things without success.
Any help greatly appreciated.
The html example in my last post didn’t format well, here is another attempt… I have had to strip out some of the brackets, so you get the idea
div class=thumbnails
div
a href=”#” title=”blah”>img src=”blah” alt=”blah” />/a
h3 a href=”blah” title=”blah”>Text to sort alphabetically ( case sensitive )/a /h3
/div
div
a href=”#” title=”blah”>img src=”blah” alt=”blah” />/a
h3 a href=”blah” title=”blah”>Text to sort alphabetically ( case sensitive )/a /h3
/div
div
a href=”#” title=”blah”>img src=”blah” alt=”blah” />/a
h3 a href=”blah” title=”blah”>Text to sort alphabetically ( case sensitive )/a /h3
/div
div
a href=”#” title=”blah”>img src=”blah” alt=”blah” />/a
h3 a href=”blah” title=”blah”>Text to sort alphabetically ( case sensitive )/a /h3
/div
/div
Sorry to get all lawyer-like on you, but is this public-domain code? You know how the lawyers want to know about intellectual property rights…
Very Nice informative, but i have some problem when i load content from external page via ajax the alphabetic sorting not working or stop. 🙁
Here is my sorting and ajax code:
r
c
g
d
b
a
s
k
click to load
$(document).ready(function(){
$(‘.ajaxtrigger’).click(function(){
$(‘#target’).load(‘demo2.html’);
});
});
li = jQuery(‘div.sort’),
inverse = true;
$(‘button’).click(function(){
li.sort(function(a, b){
return $(a).text() > $(b).text() ? 1 : -1;
});
});
Thanks for the plugin. I was getting sick of writing sorts, so it finally occurred to me to search for a nifty plugin. Yours not only suits the bill, but it has taken the concept so much further by mapping to any collection. Just love it.
I like to use object notation style plugins, so I reconfigured it in that format. I also took the cue from Dan above and included some defaults. I put the code on github as a fork of your ‘jquery-projects’. Really appreciate your contribution.