1. 01. Incoming
  2. 02. Hifi
  3. 03. Lofi
  4. 04. The Lab
  5. 05. Tags

frequency decoder

Unobtrusive Table Sort Script

Posted Friday November 18, 2005

frequency decoder

A script that sorts table rows containing date, numeric or plain-text data and then politely re-colours alternate rows

The original (server-side) solution

Update: This script has been superceded by the latest version, the Unobtrusive Table Sort Script revisited and while still available for download, is no longer being actively supported.

Thanks to everyone who chipped in with bug reports and helpfull suggestions. There’s a pint of Guinness in the virtual keg for all of you.

I’m currently in the process of rewriting a web-application that displays lots of tabular data. The previous application enabled users to sort the table rows by clicking on the table header text but unfortunately, passed the processing overhead back to the server, meaning the entire page was reloaded on each click… nasty.

The original applications table rows were “zebra striped” (again, this was originally handled by the server code), which means that a JavaScript solution has to also keep the order of the stripes after sorting & rearranging the table rows.

The JavaScript solution

This is a perfect example of where a little bit of JavaScript progressive enhancement can work wonders. The idea then was to write a script with identical functionality to the current system i.e. a script that can sort the table rows and then correctly “zebra-stripe” the rows after the sort without reloading the page.

Sortable datatypes

The script can currently sort dates (dd-mm-yyyy, mm-dd-yyyy or yyyy-mm-dd formats accepted), currency values ($, £ and €), numbers/floats and finally, plain-text (sorted in a case-insensitive manner). Additionally, the character used as the date divider (i.e. the character that separates the date parts) can be “/”, “-”, “.” or “ “ (space).

Zebra striping the table rows

Should the table be given a className of style-something, each alternate table row is given the className something after the table has been sorted i.e giving the table a className of style-alternate will tell the script to give each alternate row the className alternate.

Making any column sortable

To make any table column sortable, just give the associated table header (th) tag a className of sortable, the script will automatically determine the columns datatype, make the entire table header clickable (and not just the text contained within) and create the appropriate up/down arrow within the header when clicked (using the ↑ & ↓ characters).

Forcing a columns sort algorithm

Should a column contain a mix of datatypes (as shown in the demo’s second table), it is advised that you explicitly set the sort algorithm by adding one of the following classNames to the associated table header (th) tag: sortable-numeric, sortable-currency or sortable-date.

Stipulating custom sort functions

Should you wish to stipulate your own custom sort function, add the className sortable-yourCustomFunctionName to the table header (th) tag; for example, adding a className of sortable-myCustomSortFunction will tell the script to call a JavaScript function named myCustomSortFunction.

Additionally, the name of your custom function may only contain letters (a-Z) and the underscore character. Function names containing other characters will not be recognised by the script. The function name has been limited in this way as it is defined within a className, which itself can only contain a subset of the full ASCII character set i.e. while “$” is a valid JavaScript function name, it is not a valid character to use within a className).

Available custom sort functions

A few custom sort functions have been written and made available for those who might need them. They currently include:

  • A function to sort by IP address
  • A function to sort English longhand date formats e.g. 12th April 2006
  • A function to sort twelve-hour timestamps e.g. 12:32a.m.
  • A function to sort numbers stipulated in Scientific Notation e.g. -32.45 e 0.32
  • A function to sort percentages (positive or negative, floats or integers)

The functions can now be seen in action on their own custom function demo page.

Sorting the table automatically

As it seems to be a common request, I’ve altered the script to automatically sort any table that has been given the className sortable-onload. Tables with this className are sorted immediately after the window.onload event fires.

Should you wish to specify the column to use, give the table a className of sortable-onload-N; where N specifies an integer that defines the column to use e.g. giving the table a className of sortable-onload-3 will initiate the automatic sort using the third sortable column located by the script.

Indicating the sort is underway

Very large tables may take a while to sort which, without some sort of feedback, may leave users slightly bewildered as to why nothing seems to be happening after they have just clicked a table header.

To enable you to provide this feedback, the TH node clicked by the user is temporarily assigned the className sort-active. This enables you to create appropriate styles that visually indicate to the user that the sort is underway (adding an animated gif to the background of the TH node for example). Also, during the sort process, the mouse cursor is changed to the “hourglass”. Of course, after the sort is complete, the className sort-active is removed from the TH node and the cursor returns to it’s normal state.

Using images for the arrows

As it’s a mighty popular request, I’ve updated the script to enable images to be used in place of the arrows. The images should be defined within your CSS as follows:

      
  th span
        {
        display:none;
        } 
  th.forwardSort
        {
        background:transparent url(your-down-arrow-image) no-repeat 100% 0;
        }
  th.reverseSort
        {
        background:transparent url(your-up-arrow-image) no-repeat 100% 0;
        }    
    
  

As can be seen, you should make sure the span (which is always created by the script) is removed from the document by setting it to display:none. You are then free to style the TH node as you wish.

If you are already using background images to style the TH nodes, it is then necessary to add the class no-arrow to the table’s className (which instructs the script to add non-breaking spaces to the span element and not the “arrow” character) and move the styling of the arrows into the span element as is shown below:

      
  th span
        {
        display:inline;
        width:2em;
        height:1em;
        }
  th.forwardSort span
        {        
        background:transparent url(your-down-arrow-image) no-repeat 50% 50%;
        }
  th.reverseSort span
        {
        background:transparent url(your-up-arrow-image) no-repeat 50% 50%;
        }    
    
  

Keyboard Accessibility

The script is now keyboard accessible and (thanks to Pat), has been tested using the screen-reader Jaws.

Demo, downloads and updates

Tested in Internet Explorer 6, Opera 8.5, Mozilla and Firefox.

View the table-sort demo, the custom function demo or download the source code.

14/02/2006 :

  • Added the ‘sort onload’ functionality.
  • Integrated John Resigs addEvent function.

15/02/2006 :

  • Added the ability to ‘force’ the script to use a specified sort algorithm.
  • Added a check for identical cell data (no sort is ran should all of a columns cells contain identical data).

08/03/2006 :

  • Added the ability to specify which column the table is initially sorted on.

15/03/2006 :

  • Added the className “sort-active” to the TH node and changed the cursor to “wait” during the sort process.
  • Integrated the addEvent function into the fdTableSort object.

16/03/2006 :

  • Added the ability to specify a custom sort function.

27/03/2006 :

  • Added an innerText cache for tables with “cache-results” defined within the className.

30/03/2006 :

  • Made the script keyboard accessible.

Tags: javascript, sort, table, unobtrusive

Previous Comments ~

Ice Kapom
#1 · Monday December 26, 2005
I don’t undersatnd a f*** word !
frequency decoder
#2 · Wednesday December 28, 2005
Ice, Ice…

Once again, your English is as good as my French… it’s actually “a f***ing word”, not “a f*** word”. Pluralise your obscenities my friend, pluralise your obscenities…
Antonio · http://digilander.libero.it/odnalro/
#3 · Sunday January 1, 2006
Just great!
Wow you’re magic Brian…
frequency decoder
#4 · Monday January 2, 2006
Hey Antonio,

Happy new year my man, I hope things are going well for you.

Thanks for the comment; magic isn’t something I’m called often!

Thanks for calling…
Daniel Peck
#5 · Friday January 6, 2006
I’m having a problem implementing this in my AJAX application, however. You might know a solution, you might not. The table that I need to sort is not generated when the page is loaded. The headers are, but the contents of the table are generated by using a TrimPath template (not sure if you’re familiar) and loading the contents into the frontend. The table seems to have to exist upon pageload in order for this script to work. Is that right?

dep
frequency decoder
#6 · Sunday January 8, 2006
Hi Daniel,

The script should work with a dynamically generated table. Only the table headers need to exist on page load, not the table data. Are there any JavaScript errors being thrown ? Does the script create the arrow when the table header is clicked but not sort the rows ?

I’ve created a test page in which the tbody .innerHTML is created onload (similar to an ajax callback) and the script seems to work fine (although, using .innerHTML seems to freak Opera out). Thanks,

Brian
Daniel
#7 · Sunday February 5, 2006
Very nice script! I happened to find this just when I was looking for a fast table sort, and this fits very nicely.
Couple of points, though: instead of marking every other line of the table in the HTML source with class=”alternative”, wouldn’t it be easier to trigger a default first sort (and therefore striping) on, for example, the first column the first time the page loads? It would make the markup easier to write and make adding new entries to the table easier (no need to order them when typing the page, no need to check correct “alternate”-marking).
I’m trying to get this to work but unfortunately I’m a javascript-newbie…
oh, and on the example page the first < /body >-tag should probably be a < body >-tag ;) .

Thanks for a very useful piece of code!
frequency decoder
#8 · Monday February 6, 2006
Hi Daniel,

I actually wrote the script for a project at work that uses PHP to dynamically create the table (and therefore, dynamically create the initial “alternate” rows for me) – which is why I didn’t integrate an onload table sort.

It’s a good idea to keep adding the “alternate” rows by hand as users with JavaScript disactivated will still get a pretty striped table.

As for sorting the table onload, if multiple columns have been designated “sortable”, which one should be used to initiate the sort?

Should you wish to just stripe (but not sort) the tables onload, you can add the following method to the fdTableSort object (prepare yourself for some bad textile code formatting!):

N.B: Code removed by frequency decoder

Thanks for calling (and for spotting the error within the opening body tag),
Brian.
Daniel
#9 · Thursday February 9, 2006
Thanks for the kind reply!
The project I’ll probably use it for is an index for some review pages – it won’t be updated with new entries very often, but the user who does this is not especially skilled in the use of HTML.
Running an initial sort on the first column allows him to add new entries according to a template at the end of the page source, and they’ll be sorted in alphabetical order automatically – this way he won’t have to find the correct place and rewrite the whole page (correcting each table entry with the striped/not striped class) each time a new item is inserted in the middle. I admit it’ll not look as pretty for people who have turned of javascript, but we figure this will be such a small percentage that it won’t matter; especially since the use of table is a vast improvement on the unordered list used before.
Thanks again for the help – keep up the good work!
Travis Phipps
#10 · Friday February 10, 2006
First: I LOVE this script. It is definitely much cleaner than my old method of requiring page refreshes.

I stumbled on what I would consider a bug. If a column has both text and numeric data in it, the sort doesn’t work correctly. It appears to randomly reorder to rows. Can something be done to correct this behaviour?
frequency decoder
#11 · Saturday February 11, 2006
Hi Travis,

In fact, the script attempts to determine the columns datatype in the following order:

1. Date
2. Numeric
3. Currency

If none of the above datatypes are found, it then defaults to a case-insensitive character sort.

The type of sort used then depends on the data found within the columns first non-empty cell. If it’s numeric, the numeric sort algorithm is used (which will return ‘quirky’ results if other cells contain plain text). If it’s plain text, the case-insensitive sort is used which sorts cell data by comparing character code values. Again, this may seem to return ‘quirky’ results if the column contains numeric data but in fact, is a true representation of a character code sort.

I should really alter the script to test that every column cell has the same datatype before determining the sort algorithm used. A task for next week then!

Thanks for the comment – I’m glad that you like the script.

Brian.
frequency decoder
#12 · Tuesday February 14, 2006
@ Daniel: I’ve updated the script to automatically sort tables that have been given the className “sortable-onload”. You can download the new source from the demo page. Hope this helps…

@ Travis: If the column is found to contain both Numeric and Plain-text data, which algorithm should be used for the sort? It’s not uncommon for columns to look something like this:

12
34
n/a
54.3
n/a
44.7

How should the script determine the column datatype in this case? The only way I can think of is to determine if the column contains more numeric cells than plain-text cells before choosing the sort algorithm to use. This will still not work in all cases e.g. if the following column was encountered

12
n/a
n/a
n/a
3.4

The script would determine that the column contains plain-text data and not numeric.

The only other way I can think of is to enable the user to force an algorithm by giving the TH tag a specific className (‘sortable-numeric’ for example). I’ll keep you updated should I implement this change…

Brian
Travis Phipps
#13 · Tuesday February 14, 2006
hmm. All good questions! I’m not really sure the best method. I think you’re probably on the right track with allowing the user to force a particular sort type using a specific class name.

One other issue I ran into was with data like the following:

c:\Inetpub\wwwroot\appname\includes\TinyAjax.inc.php
/appname/includes/dummyfile.php
/appname/test.php
c:\inetpub\wwwroot\appname\anotherfile.php

I’m sure it’s the forward slashes that are throwing things off, but this column of data has the same random sort issue.

Oh, and one more….If a column has all the same data, for instance:

test
test
test
test

The script appears to also do a random sort. Could you add code to just not do anything (except maybe show the arrow) if all cells contain the exact same data?

OK, I think that’s all for now. Thanks for such quick responses!!
frequency decoder
#14 · Wednesday February 15, 2006
@ Travis: I’ve integrated both of your suggestions into the script. You can now force a sort algorithm by adding the appropriate className to the associated table header (th) tag (see “Forcing a columns sort algorithm” above).

I’ve also added a check for identical cell data. Should the columns cells all contain identical data, the arrow is drawn but no sort is ran.

Rather suprisingly, it only took 10 lines of code to achieve both of the changes, so the scripts network footprint hasn’t risen dramatically (which was my initial worry)!

Again, thanks for the interest,

Brian.
Travis Phipps
#15 · Wednesday February 15, 2006
Looking good. But I think the “identical” check may need a little work. I have a column that looks like:

test
test
test

And it doesn’t sort at all. The is actually an empty cell. I’m guessing since the script doesn’t see any data, it just completely ignores it. Is there any way around this one?

Again, thanks so much for your prompt response!
frequency decoder
#16 · Thursday February 16, 2006
@ Travis: You may just be right about the “identical” check (which serves me right for not fully testing the code!).

I’ll see what I can do to fix it (perhaps during my lunch hour today, if not, during the weekend – well, sunday morning to be precise).

Regards,
Brian.
frequency decoder
#17 · Thursday February 16, 2006
@ Travis: I’ve fixed the “identical” check. It should work as intended now.

Take care,
Brian
Travis Phipps
#18 · Friday February 17, 2006
Hmm. I didn’t notice a change. I still have problems with columns of data that have blank cells.

i.e.:

test
test
test
(blank cell)
(blank cell)

This column of data doesn’t sort even with the latest version. I realize it’s not THAT big of a deal since most of the cells are the same, but just thought I’d trow it out there.
frequency decoder
#19 · Tuesday February 21, 2006
Hi Travis,

I can’t seem to reproduce the problem you are having with empty cells. I’ve created another test page that seems to work:

Identical and empty cell test

I’ve checked the page in Firefox and IE6 and the columns sort properly – If your still experiencing problems, drop me an email…

Regards,
Brian.
Nathan Logan · http://nathanlogan.com
#20 · Tuesday February 21, 2006
Not only are you magic (on the mic), you’re also dope on the floor. And according to MC Hammer (and to the rest of us), that’s a good thing.

Thanks SO much for this script. It’s a true blessing for our site (used at /resources/sermons/ and /resources/reviews/).

Hat’s off to you. You’re a genius. PayPal donate, anyone?
frequency decoder
#21 · Wednesday February 22, 2006
Hey Nathan,

Good to hear from you!

Thanks for the kind words… unfortunately, I now can’t seem to shake the MC Hammer song from my head… Oh-oh-oh-oh-oh-oh-oh… I’m going to have to play some Bonnie Prince Billy to clear “can’t touch this” from the neural pathways.

Also, thats a rather spiffy site design – did you do it?

Take care my man,

Brian.
Nathan Logan · http://nathanlogan.com
#22 · Wednesday February 22, 2006
Brian, sorry about the Hammer reference, but hey, you’re the embodiment of those lyrics – I was just pointing out the obvious.

Regarding the design, while I would love to take credit for it, I cannot. At least not mostly. The frontpage design was put together by Cody Lindley (a good friend of mine). I took a couple liberties, but he is really responsible for the creativity there. I’m glad you like it.

Keep up the good work. Thank you so much for sharing this stuff with “the community”. After coming to your site for the first time about a week ago, I was an instant fan. One more thing: add a PayPal donate button. =)
Dominique
#23 · Wednesday February 22, 2006
Hi, really great script, works fine .. except I’m a bit crazy and my table is 7 columns wide with 4 columns sortable and almost 5.000 lines deep !!

It seems browsers have a bit of problems with that.. it takes so much processor power to sort that I get 4-5 warnings telling me the script is not responding.. but after 2-3minutes it did the sorting..

I know it’s probably crazy.. but do you have a solution to speed up things ?

Dominique
frequency decoder
#24 · Thursday February 23, 2006
@ Nathan: Ah-ha (not the 80’s pop group of the same name), so Cody’s the main man! Should have guessed! You know, I was thinking about adding a PayPal donate button but I just don’t feel “right” about it… perhaps one day when I’ve come to terms with the fact that people are actually using the scripts I write.

@ Dominique: Wow, 5000 lines… thats a lot of JavaScript processing! I suggest you remove the script completely and sort the data on the server. It’s probably not the answer you were looking for but in reality, it’s the only solution that will work should you wish to display the table in full (the script is pretty much as fast as it can be). Apologies.

If server-side processing isn’t an option, why dont you paginate the table i.e. split the table into more managable chunks. This way, users don’t have to download the entire table (at 5000 lines, thats a hefty network footprint). It may mean more .html files to deal with but it also means smaller downloads and, of course, that the script can be used.
dominique
#25 · Sunday March 5, 2006

Hi, not to use my 5000 lines at once I’m trying to include them within “tbody .innerHTML” (with an XMLhttprequest), but it only works in firefox.. not IE6. Your example page above also doesn’t work on IE6). It seems one can only do a replacement inside a “DIV id=..” not inside a table.

Do you have any ideas how to solve that ?

frequency decoder
#26 · Monday March 6, 2006

Hi Dominique,

Internet Explorer and Opera are quite finicky about dynamic row/cell creation. In fact, they require the use of the insertRow and insertCell methods and tend to freak out when innerHTML is used in this way.

Your JavaScript AJAX will have to return a JSON result that can be iterated over. I’ve changed the dynamic demo to do just that (using a fake AJAX JSON result) - it appears to work in Internet Explorer 6 now.

Have a look at the page source to see how the JSON result is added to the table. For more information on insertRow and insertCell methods, see the MS documentation

Also, ParticleTree have a very relevant article on preloading data with Ajax that you should try to read.

Hope this helps some. (N.B: The dynamic test above was only to show that the script does work with dynamically created tables – it was the use of innerHTML that made it fall over in IE and Opera, not the actual tableSort script).

Regards,
Brian.

Matteo
#27 · Thursday March 9, 2006

Hi, good works !!
A question: How can I set which column must be sorting automatically ?

Bye
Matteo

frequency decoder
#28 · Thursday March 9, 2006

@ Matteo: I’ve just added functionality to allow you to specify the column to use. Read the paragraph entitled “Sorting the table automatically” to understand how to specify the column.

Thanks for the idea - it only took two lines of code to achieve.

Regards,
Brian.

Matteo
#29 · Thursday March 9, 2006

Good !! Very thanks !!
I’ll try to ask another question: Do you think is it possible to sort the table after another javascript event (an example in other part of the page) ?

Bye
Matteo

frequency decoder
#30 · Thursday March 9, 2006

@ Matteo: It is possible, just call the Object method fdTableSort.initSort, passing a reference to the TH node of the column you wish to sort as a parameter i.e.

var TH = document.getElementById(‘aTHnode’);
fdTableSort.initSort(TH);

Hope this helps,
Brian

Matteo
#31 · Thursday March 9, 2006

Now it’s Perfect !!
Thank you very much !!
Matteo

Simon Claret · http://ladd00.triumf.ca:3000/list/6
#32 · Friday March 10, 2006

I’ve been integrating your (awesome!) script into a rails ruby on rails app recently (http://ladd00.triumf.ca:3000/list/6). My problem is similar to daniel’s, in that the tablesort script doesn’t work with a table that is loaded via ajax.

My app doesn’t just keep the headers and reload the table body, it actually reloads the entire table when you click ‘Show retired items (3)’ or ‘Show all data fields’. At that point, I’m guessing that the javascript is still pointing at the old table, which no longer exists, and doesn’t even know about the new table that’s been loaded in.

Rails provides a javascript callback that fires as soon as the ajax call is over. Would it be possible to call something in your script through that callback, instructing it to re-scan the page for the table? Or maybe there’s a better way to do this…

frequency decoder
#33 · Friday March 10, 2006

@ Simon: Hi Simon, you could try to call fdTableSort.init(). This should reinitialise the script and reinsert the necessary events etc onto the new TH nodes.

I would worry about Internet Explorer memory leakage though. Every time the script initialises, it adds an onClick event handler to each TH node with a class of “sortable”. If these nodes are being removed by the Ajax callback without first removing the old event handler, this could lead to memory leaks with each Ajax callback.

This is just a theory though – perhaps I’m wrong (I hope I’m wrong)!

Regards,
Brian.

Matt
#34 · Saturday March 11, 2006

This works great!
Is there a way I can remove the arrow all together?
My javascript is crap and I can’t really work it out. When I click on the table header text it causes the table header row to expand in height, which puts the formatting all out :(
I don’t know what’s causing it!
Please help

frequency decoder
#35 · Saturday March 11, 2006

@ Matt: Just add the following CSS rule and hey presto, no arrow (it’s still created by the script but the following rule removes it from the display):

th.sortable span

{ display:none; }

Regards,
Brian.

Matt
#36 · Sunday March 12, 2006

Ah!
Thanks heaps mate. Nice work, it does EXACTLY what I need. Legend!

Matt
#37 · Monday March 13, 2006

Actually, one more thing :P
Can you exculde certain columns from being sorted?
Eg. I want to sort the first 5 columns, but exclude the next 2 columns. There’s only 8 rows

It’s no biggie, so if it’s too hard don’t worry about it.

Thanks again.
Matt

frequency decoder
#38 · Monday March 13, 2006

@ Matt: Columns are only sorted if they have been given one of the following classNames “sortable”, “sortable-numeric”, “sortable-currency” or “sortable-date”.

Any column that has not been given one of the listed classNames doesn’t get sorted…

regards,
Brian.

Matt
#39 · Tuesday March 14, 2006

[th class=”sortable”>col1[/th>
[th class=”sortable”>col2[/th>
[th class=”sortable”>col3[/th>
[th class=”sortable”>col4[/th>
[th>col5[/th>
[th>col6[/th>

Take the above for example. By clicking on col1-4 it will sort, but I don’t want it to move the col5-6 data. It’s as if you are freezing col5-6.
Sorry I should have explained it better the first time.

frequency decoder
#40 · Tuesday March 14, 2006

@: Matt: Ah-Ha, understood!

Sorry, it would take an immense amount of code to achieve so I’m afraid I’ll have to pass on this one. Apologies…

Regards,
Brian.

Matt
#41 · Wednesday March 15, 2006

No problem, I’m happy the way it is. Just had to ask :)

Thanks again!

KenLin
#42 · Wednesday March 15, 2006

Is there a way to change the cursor to ‘wait’ while the sorting happens? On large tables, it’s not obvious anything is happening until it happens.

Thanks for the script, it’s robust and super-easy to use

frequency decoder
#43 · Wednesday March 15, 2006

@ KenLin: I’ve altered the code to do just that (You won’t notice the cursor change within the demo as the tables are small).

Additionally, I’ve added the class “sort-active” to the TH node clicked by the user. This allows you to style the TH node any way you so desire (adding an animated gif to the background for example) while the sort is being calculated. Of course, this class is removed once the sort process has completed.

Hope this helps…

Regards,
Brian.

Michael
#44 · Thursday March 16, 2006

This looks like what I’ve been searching for!

But, your demo does work perfect with older releases of Firefox 1.0.4, “zebra striping” is broken.

The only thing I am missing is a way to specify a custom sort-function. Did I miss something?

frequency decoder
#45 · Thursday March 16, 2006

@ Michael: I’ll look into the Zebra Striping issue (a strange issue as I’m just adding the appropriate className to the TR node). As for a custom sort function, it’s an idea I didn’t think of so it’s currently not supported.

It’s a good idea though, so, if it doesn’t take immense amounts of code to achieve, I’ll try to add the functionality…

Update: It only took one line of code to achieve, so I’ve already integrated the functionality into the source code. Hope this helps.

Thanks for the interest,
Brian.

Pat
#46 · Thursday March 30, 2006

I was wondering what I would need to add so that a visually impaired user could tab from column to column. They could then pick the column to sort. I haven’t started to implement your code because generally if I can’t tab through it won’t work for jaws users. I simply love all the features and want to use it for a few of our html table pages. Thanks. And get that donation button out there!

frequency decoder
#47 · Thursday March 30, 2006

@ Pat: I have been wondering about this myself…

My original idea was to create a link within each sortable TH node (containing the original TH nodes text of course) that, when activated, called the appropriate JavaScript to initiate the sort but… this would involve creating a link with an HREF like href=”#” and I’m not at all sure how Jaws etc handles links of this type i.e. will Jaws reset the read point to the top of the page even if the JavaScript attempted to cancel the event (e.g. return false)?

I shall try to look into this today and if Jaws etc will respect the “stop event”, I’ll try to integrate it into the code.

As for the Paypal button, you’re the third person who has mentioned it so… I might just look into that as well (hey, it might even keep the kid in nappies/diapers for a week!).

Thanks for the interest,
Brian

Pat
#48 · Thursday March 30, 2006

Brian,

I thought I posted this earlier but I guess I didn’t finish the update.

This is what our JAWS expert told me when he checked out the script and our correspondence.

“There shouldn’t be a problem with creating a link with the href=”#” and the return false event. JAWS does a good job of keeping the focus where it should be when you use this method (as opposed to heading back to the top). ”

Thanks, Pat

frequency decoder
#49 · Friday March 31, 2006

@ Pat: Thanks for the Jaws info!

I've a working version that creates anchors within the sortable TH nodes (as described in my last comment) that I shall upload this weekend (sorry, I've no FTP access from work today).

It uses the onkeypress event to check for keyboard events (pressing “Enter” initiates the sort, pressing “Tab” moves to the next anchor tag as expected).

I’ll upload it as a separate demo and if you think it’s a winner, I’ll roll the changes into the main code.

Again, thanks for the Jaws info - there is suprisingly little info out there with regard to Jaws and JavaScript.

Regards,
Brian.

Pat
#50 · Friday March 31, 2006

That’s great Brian. We’ll check it out. You’re right, there’s not much information on JAWS and JavaScript.

Pat
#51 · Monday April 3, 2006

Brian, did you run into an obstacle? I didn’t see the demo link. Thanks. Pat

frequency decoder
#52 · Monday April 3, 2006

No, no obstacle – just work, babies, vicious “toy” dogs and an insane public transport system populated by an even more insane assortment of the French general public… but thats another story altogether.

Heres an initial attempt at a keyboard accessible version of the script.

I’ve only tested it so far in Firefox and IE6. Hope it plays nicely with Jaws.

Regards,
Brian.

Pat
#53 · Tuesday April 4, 2006

Brian,

Here’s what our Jaws expert said.

“That works a lot better from a keyboard standpoint. In looking at the script behind it I’d make a small modification to let the JAWS user know which column they are sorting on in the title attribute itself. Usually JAWS defers to looking strictly at the title attribute or the text itself (not both) – so it’s usually safest to let ‘em know in the title attribute. Here was the line I saw in the script that I modded slightly for that problem:

Current Script:

Line 89 – a.title = “Sort on this column”;

Modified Script:

Line 89 – a.title = “Sort on ” + thtext;

Frequency coder already had a variable housing the column name (thtext) so I just substituted that for the “this column” text.”

His only other question was how would the jaws person know which way the table is sorted. He didn’t know if this was even an issue since most tables don’t tell you.

I’m ready to use the script!

Thanks.

frequency decoder
#54 · Wednesday April 5, 2006

@ Pat: Many thanks for the Jaws testing.

I’ll roll the changes/recommendations into the main source code and update the demo accordingly.

Also, I currently create the “arrow” within the anchor tag but may create it as a child of the TH node – which would make CSS styling easier to achieve.

Update: I’ve rolled the changes into the current source code - and the arrow is now created as a child of the TH node.

Regards,
Brian.

Pat
#55 · Wednesday April 5, 2006

Brian,

You’re the best! We are going to start rolling the script out this week.

Pat

theBag
#56 · Saturday April 8, 2006

Great script. Just one thing is bothering me before I can roll out my project. I have a table of Films that will be shown at a festival. My main problem is sorting by time. Currently the format is in 12-hour time. I display times like this:

8:00pm, 8:00am

Now you can see my sorting issue, dealing with am/pm. Is there an easy way around this? I`m sooo close, I just have this one little problem.

BTW – I wish I found your website earlier, the stuff I could have used when I had problems… owell :)

frequency decoder
#57 · Saturday April 8, 2006

@ theBag:The script allows you to call custom sort functions for specific columns. You could try the following:


function sortByTwelveHourTimestamp(a, b) {
        // Get the innerText of the TR nodes
        var aa = fdTableSort.getInnerText(a.getElementsByTagName("td")[fdTableSort.pos]);
        var bb = fdTableSort.getInnerText(b.getElementsByTagName("td")[fdTableSort.pos]);

        // If one am and the other pm then the pm is greater
        if(aa.search(/a[\.]?m/ig) != -1 && bb.search(/p[\.]?m/ig) != -1) return -1
        else if(aa.search(/p[\.]?m/ig) != -1 && bb.search(/a[\.]?m/ig) != -1) return 1

        // Check at least one integer exists
        if(aa.search(/[0-9]/g) == -1 && bb.search(/[0-9]/g) != -1) return -1;
        else if(aa.search(/[0-9]/g) != -1 && bb.search(/[0-9]/g) == -1) return 1;
        
        // Remove leading "12" if any (12 is before 1 timewise)
        aa = aa.replace(/^12/,"");
        bb = bb.replace(/^12/,"");

        // Both am or pm so remove the non numeric characters and treat as integers
        aa = aa.replace(/[^0-9]/g,"");
        bb = bb.replace(/[^0-9]/g,"");

        if(aa == "") aa = 0;
        if(bb == "") bb = 0;
        
        return aa-bb;
}

Update: I’ve changed the demo to use this function as it’s “custom sort function” so you can see it in action.

It allows the am/pm to be written in various ways e.g. am, a.m, a.m., AM etc. It also enables you to have spaces between the hour and the am/pm parts and the hour/minute dividor can be a full-stop or colon. Additionally, it takes into acount that 12.30am is actually before 1.00am and checks to see if a non timestamp has been passed in (allowing you to place data like “Unknown” within a table cell for any film that hasn’t yet been scheduled or whose start time is currently… well, unknown…)

Regards,
Brian

Pete · http://www.relaxed-high.de
#58 · Sunday April 9, 2006

Hi Brian,
thank you so much for your wonderful script. I have just one hint: If you have a table header with two rows (and some rowspans) and you sort on a column in the second row, then it does not erase the arrow any more.

Do you know how to resolve that?

Greetings,
Pete.

frequency decoder
#59 · Sunday April 9, 2006

@ Pete: Hi Pete, in fact, the script was written to handle one table header per column – which makes sense, as if there are two columns assigned to one table header, how will the script know which of the two columns to perform a sort on, the first or the second?

You could add another classname to the TH tag, for example, “sort-column-2” (which would tell the script to sort on the second of the two columns) and alter the script accordingly.

Unfortunately, I currently haven’t got the time to attempt it myself – apologies (but it may be something I look into later).

Should I change the script to take rowspans into account, I’ll mail you an updated version.

Again, sorry I can’t be more helpful.

Update: I’ve completely misread your comment and thought colspans instead of rowspans. Unfortunately, the same response applies, I just don’t have the time to look into it.

Regards,
Brian.

theBag
#60 · Tuesday April 11, 2006

Brian,

Apologies for the delay in thanking you! Thanks!

You can see my rendered table here:
http://sydfilmfest.sitesuite.ws/page/search_results.html

All I need now is to be able to sort by venue that is residing in the same column as time. But I have delegated such a task to my developer. He seems to think its possible.

Thanks again for your work.

frequency decoder
#61 · Tuesday April 11, 2006

@ theBag: Hey, no problem, glad to help…

I was at the Sydney film festival (for animated films) a few years ago that was held in the park… very good indeed. The highlight had to be Gabriel Byrne (a fellow Irishman), drunk as a skunk and dressed in a white linen suit presenting the awards. Goes to show – never let an Irishman near the “Greenroom”!

Regards,
Brian.

Pete · http://www.relaxed-high.de
#62 · Monday April 17, 2006

Hi Brian,
I just got rid of my second header row. But now I have another question: How would you sort currencies in the Continental European format?

For example: 1.000,50

Point for the thousand and comma for the cents.

Cheers,
Pete.

frequency decoder
#63 · Tuesday April 18, 2006

@ Pete: You could try to write your own custom sort function. I’ll start you off (I’m typing this live into the comment box so don’t actually know if there are any JavaScript errors)


function sortContinentalCurrency(a, b) {
        // Get the innerText of the TR nodes
        var aa = fdTableSort.getInnerText(a.getElementsByTagName(“td”)[fdTableSort.pos]);
        var bb = fdTableSort.getInnerText(b.getElementsByTagName(“td”)[fdTableSort.pos]);

        // Replace the ”.” with nothing and the ”,” with a ”.” and try to return a float
        aa = parseFloat(aa.replace(”.”,””).replace(”,”,”.”));
        bb = parseFloat(bb.replace(”.”,””).replace(”,”,”.”));

        if(isNaN(aa)) return -1;
        else if(isNaN(bb)) return 1;        

        return aa-bb;
}
        

Give the above function a whirl and get back to me if it falls over dramatically.

Regards,
Brian.

Hariharan
#64 · Tuesday April 25, 2006

Hey decoder:
Sounds like a great script with lots of cool features.

I am planning on trying it out for the project i am doing.

Are you planning to adding scrollable feature to the tables so the header is always on the top.

Have you also done any benchmarking in terms of when this solution becomes inefficient with respect to server side sorting?

Matt
#65 · Tuesday April 25, 2006

For someone that wants to use their own up/down arrow graphics, I think this does the trick:

arrow = document.createElement('img');
arrow.setAttribute('src','/images/sortup.gif');
...
arrow = document.createElement('img');
arrow.setAttribute('src','/images/sortdown.gif');
...
span.appendChild(arrow);
frequency decoder
#66 · Tuesday April 25, 2006

@ Hariharan: I don’t think I’m going to add the sticky header feature as I want to keep the script as light as possible. A static header technique has recently been shown over at Cody Lindley’s site that you may be interested in (which involves no JavaScript).

As for benchmarking… it all depends on the type of data within the table (dates take longer than Integers to sort etc). No JavaScript solution that I have seen can handle really large tables, which indeed, might be better off sorted server side.

@ Matt: I’ve a new version of the script that enables you to define your arrow images within the CSS. This way, I’m not creating hardcoded images within the JavaScript and changing the arrows can be achievied by changing the stylesheet (which might be usefull for people using stylesheet switchers). I’ll try to get the new version of the script up this week.

Thanks for the comments.

Update: I've changed the script to enable images to be defined within the CSS.

Regards,
Brian.

Gregor
#67 · Wednesday May 3, 2006

Hi,
looks great, is a how to or a step by step example for “web starter” aviable?
especial: the way to implement the script in a existing php file with a table.

Thanks a lot,
Gregor

DarkSide · http://exalted.be
#68 · Saturday May 6, 2006

I implemented this in 1 minute, in a table generated by php, very easy !!!

Thank you sooooo much

frequency decoder
#69 · Tuesday May 9, 2006

@ Gregor: Hi Gregor, I currently don’t have the time to write a full “how-to” tutorial (I’m just back from vacation and have a two week backlog of work to get through – nice!). You should view the page source of the demo to see how to include the necessary javascript file within your HTML page and also how to designate table columns as sortable (check out the classnames on the TH tags).

If your really stuck, send me the PHP code that creates the table and I’ll get it working for you…

P.S. As it’s a 100% JavaScript solution, it makes no difference to the script if the table is created by PHP/ASP etc, the script just needs to find the correct classnames within the TH tags.

Apologies for the rather unhelpful reply, I shall add the tutorial to my TODO list for future reference though!

@DarkSide: Thanks, glad it helped you out!

Nils
#70 · Thursday May 11, 2006

Hi Brian,

First of all….THANKS!!! Love this script Before this I was still doin’ serverside sorting.

I’ve seen a couple op those, but so far this one was the only one where you could choose if a column has to be sortable or not, so again thank you very much.

Best regards, Nils

If you don’t mind, I’ll drop a question.
I’m trying the feature were I can choose my own image(s) instead of the arrow(s)

So I add the extra lines in the stylesheet as you described, but nothing happened. Not the regular arrows or the images show up.

You can see my example at: http://www.vanzijderveld.nl/sorttable/table-sort.htm

Did I miss something?

Thanks in advanced

frequency decoder
#71 · Thursday May 11, 2006

@ Nils: Hi, glad the script is of use!

I think (fingers crossed) the problem lies with the css, try changing the last three rules to read:

table.generique th span
{

display:none;
}

table.generique th.forwardSort
{

background:transparent url(sort_down.gif) no-repeat 100% 0;
}

table.generique th.reverseSort
{

background:transparent url(sort_up.gif) no-repeat 100% 0;
}

i.e. add the “table.generique”.

If this doesn’t solve the problem then leave another comment and I’ll look into it for you (perhaps it’s a bug in the code).

Regards,
Brian

Nils
#72 · Thursday May 11, 2006

Hi Brian,

Thanks for the quick response!

Your suggestion makes the images show! So that’s allright. But now something else comes up:

If you sort on a column and you sort the same column again (ASC/DESC) and THEN you sort on another column, the image on the first column will stay!!!

So at this point, it is possible to show on all columns-headers the “sort-image” (By clicking twice on each column)

It looks like a small bug to me???

You can test it again on: http://www.vanzijderveld.nl/sorttable/table-sort.htm

Anyway, thanks so far!

Nils

frequency decoder
#73 · Thursday May 11, 2006

@ Nils: Yep, I spotted that one myself… the culprit is the following line within the code (line 154):

th.className = th.className.replace(‘reverseSort’,’’);

It should read:

th.className = th.className.replace(/forwardSort|reverseSort/g,’’);

I’ll upload the changed code a.s.a.p

thanks again,
Brian

Blake Brenton
#74 · Friday May 12, 2006

I am currently using your javascript on a table.
the table has a lot of contents to it.
it takes 8 seconds to load and the hourglass isn’t being displayed
could you either answer why the hourglass doesn’t work or give me some sample code to add a
.gif to the header column while the sorting in being processed

thanks

Nils
#75 · Friday May 12, 2006

Yep, that’ll do the trick!

Thank you very, very much for the quick response!

Greetings from the Nertherlands :)

Nils

Edit: No problem Nils, glad to be of help. I should say to all and sundy though that the CSS used for the demo was taken lock-stock-and-barrel from an application I'm currently writing at work - which is why the "table.generique" references exist.

The use of this CSS declaration is not necessary and in fact, I suggest that it's removed from the CSS alltogether.

frequency decoder
#76 · Friday May 12, 2006

@ Blake:

Q: Why isn’t the hourglass being displayed?
A: I have no idea.

Q: Give me some sample code to add a .gif to the header column while the sorting in being processed.
A: The class “sort-active” is added to the TH node during the sort. Change your CSS to reference the .gif e.g.

th.sort-active {
background: transparent url(yourImage.gif) no-repeat 100% 50% !important;
}

regards,
Brian.

Nils
#77 · Friday May 12, 2006

Hi Brian,

I’m back again (hope you don’t mind)

After testing I noticed that the “sortable-date” class is sorting on mm/dd/yyyy or yyyy/mm/dd.

But what if I have the european format dd/mm/yyyy or even dd/mm/yy

In this format the sort-function is not correct?

Is it possible that you could somewhere declare a specific format?

Hope you can help again?

Greetings from the Dutch,

Nils

frequency decoder
#78 · Friday May 12, 2006

@ Nils: You could write your own custom sort function or change the current one to test for the European format before the American format within the method “dateFormat” (it currently tests in the following order mm-dd-yyyy, dd-mm-yyyy and finally yyyy-mm-dd).

Regards,
Brian.

Travis
#79 · Friday May 12, 2006

I am having an issue on a colimun that is using sortable numeric class when it sorts 0 and negative numers it puts 0 below negative numbers have you seen this before

frequency decoder
#80 · Saturday May 13, 2006

@ Travis: Hi Travis, it was a really dumb piece of logic within the sortNumeric method. I’ve changed the method in question (and also fixed the same error within the sortCurrency method). Things should work as intended now.

Regards,
Brian.

Blake Brenton
#81 · Monday May 15, 2006

Hi Brian
I’m a beginner when it comes to javascript and i haven’t worked with css before
i was just wondering, the sample css code you wrote for me, where should it be enter in your code?

Thanks
Blake

frequency decoder
#82 · Monday May 15, 2006

@ Blake: Hi Blake, I suggest you view the demo source code.

You will see that the CSS declarations for the page are all encapsulated within “style” tags within the “head” of the HTML document e.g.

[style type=”text/css”]
table.generique

{ border-left:1px solid #C1DAD7; font-size:1em; width:96%; border-spacing: 0px; empty-cells:show; margin:0 auto 1em auto; text-align:left; padding:0; }

... lots more CSS …

[/style]

If this is how you are adding CSS to your page, just add the new CSS rule to the end of the list e.g.

[style type=”text/css”]

... the original CSS …

table.generique th.sort-active {
background: transparent url(yourImage.gif) no-repeat 100% 50% !important;
}
[/style]

If you are using external stylesheets, just add the new rule to the external stylesheet.

If your stuck, send me the HTML page (to the email address shown within the site footer) and I’ll get it working for you.

Hope this helps,
Brian.

P.S. Blake, you previously said that your table takes up to 8 seconds to sort. To be honest, I think that tables of this magnitude are better off sorted on the server as an 8 second wait equates to a terrible user experience. Just my 2 cents!

Travis
#83 · Monday May 15, 2006

That did the trick can you tell me how to modify the script so the first time it sorts it does descending first for numeric values.

Thank you so much this script is amazing

Nils
#84 · Tuesday May 16, 2006

Hi Brian,

Yeah, you’re right, I change the sequence of the dateformat and that was it!
Thanks again for pointing it to me!

good luck,

Nils

frequency decoder
#85 · Tuesday May 16, 2006

@ Travis: Thats a tricky one. The script sorts in descending order by first sorting in ascending order and then simply reversing the sort (using the .reverse method of the array object) so I think you will have to try to tinker with the code to get this to work.

@ Nils: No problem, it’s probably something I should have documented anyway.

James · http://www.jws.co.za
#86 · Tuesday May 16, 2006

Hi Brian

This is an awesome piece of code!

I am battling to get the 'hour glass' to work. Here is the last few lines of my css file:

Edit: CSS removed by frequency decoder

Any ideas?

Thanks,
James

Update: Hi James, I've updated the code with a fix for the hour glass problem.

michael
#87 · Sunday May 21, 2006

hey brian
this code i think does exactly what i need , but i experienced a problem when i downloaded it, i recieved a microsoft script host error “window” is not defined”. has this happened to anyone else?

frequency decoder
#88 · Sunday May 21, 2006

@ Michael: It sounds as if you attempted to “execute” the .js file that you downloaded. As it’s not included within an HTML document at this stage, Windows will display the error in question.

Don’t worry, it’s not an error with the script!

Regards,
Brian.

John
#89 · Wednesday May 31, 2006

great code, just wondering if its possible to get it working with a php table that has recordset paging.. i have got it working when all records are shown in the recordset but when i show only 10 for example, the sort only sorts through those ten, i would like to sort the whole recordset,

any ideas?

cheers
John

frequency decoder
#90 · Wednesday May 31, 2006

@ John: The script can only sort table rows that are already present within the HTML. A script that deals with a paginated table will require some sort of Ajax request to the server etc, something that this script was never meant to accomplish.

You could always create the entire table within the HTML and only show 10 rows at a time (hiding the other rows with the CSS style display:hidden; for example) but this would require a complete rewrite of the current script.

Sorry I can’t be more help.

Regards,
Brian.

Dany · http://www.escoplanes.com.ar
#91 · Monday June 12, 2006

Hi, I need that the last row in the table remaining the LAST after any column sort, is it possible?. I’m a newbie in javascript, but if you can recognize the total rows is possible to sort total-1 rows???

frequency decoder
#92 · Wednesday June 14, 2006

@ Dany: Hi Dany, just wrap the last row within a TFOOT and the script will leave it alone.

Thanks for the comment,
Brian.

jak
#93 · Tuesday July 4, 2006

Hi,
First of all I have to compliment you for the great work you’ve done here. I have been testing the script this afternoon but could not make exactly what I wanted to do.
Here is the question: how can I make the table be striped when the page is displayed without having to make a “sort” call. In fact the way the table is displayed without any sorting is the way I want it to be on load. So if I put “sortable-onload” it will sort the table (and hence stripe it ) yet will not be displayed as I want it to be initially.
Thank you very much for any suggestion, and again great work.

frequency decoder
#94 · Tuesday July 4, 2006

@ Jak: Hi, unfortunately, the zebra-striping and sort actions are both contained within the same method so it’s currently impossible to just stripe the table onLoad. Should you wish to only zebra-stripe the table (and have no need at all for the sort), you should check out the 15 Days of jQuery site that has this functionality written in under 3 lines of code.

If your JavaScript savvy, you could also change the current script to contain the striping mechanism within it’s own method (it’s the last 4 lines of code within the initSort method).

If you email me, I can alter the code for you and send you on the new version as an attachment.

Hope this helps,
Brian.

brian
#95 · Friday July 7, 2006

God bless you

Pete
#96 · Saturday July 22, 2006

Brian,

if have a column with these values: A1, A100, A23, A230 …
Currently, the cool script is sorting it like that: A100, A1, A230, A23 …

How would you customize the script to have this order: A1, A23, A100, A230 …

Regards,
Pete.

frequency decoder
#97 · Saturday July 22, 2006

@ Pete: Hi Pete, it’s just a simple matter of removing the leading “A”. Try this (written directly into the comment box so it might just have syntax errors!):

function sortPete(a, b) {

// Get the innerText of the TR nodes var aa = fdTableSort.getInnerText(a.getElementsByTagName(“td”)[fdTableSort.pos]); var bb = fdTableSort.getInnerText(b.getElementsByTagName(“td”)[fdTableSort.pos]); // Replace anything but numbers aa = aa.replace(/[^0-9]/g,””); bb = bb.replace(/[^0-9]/g,””); if(isNaN(aa)) return -1; else if(isNaN(bb)) return 1; return aa-bb;
}

Hope this helps,
Brian

Pete
#98 · Saturday July 22, 2006

Thanks Brian,

and sorry for my imprecise explanation. My column has values like:
A1, B200, A150, B2 …

And the script should sort it like: A1, A150, B2, B200 …

Do you also know how to solve this?
Pete.

frequency decoder