Quoi

Vous aimeriez que vos tableaux soit triable, par clic sur la ligne d'en-tête ?
Avec ce JS rien de plus simple !
Rendre vos tableaux triable est un jeu d'enfant !

Code

javascript
addEvent(window, "load", sortables_init);
 
var SORT_COLUMN_INDEX;
 
function sortables_init() {
    // Find all tables with class sortable and make them sortable
    if (!document.getElementsByTagName) return;
    tbls = document.getElementsByTagName("table");
    for (ti=0;ti<tbls.length;ti++) {
        thisTbl = tbls[ti];
        if (((' '+thisTbl.className+' ').indexOf("sortable") != -1) && (thisTbl.id)) {
 
            //initTable(thisTbl.id);
            ts_makeSortable(thisTbl);
        }
    }
}
 
function ts_makeSortable(table) {
    if (table.rows && table.rows.length > 0) {
        var firstRow = table.rows[0];
    }
    if (!firstRow) return;
 
    // We have a first row: assume it's the header, and make its contents clickable links
    for (var i=0;i<firstRow.cells.length;i++) {
        var cell = firstRow.cells[i];
        var txt = ts_getInnerText(cell);
        cell.innerHTML = '<a href="#" class="sortheader" onclick="ts_resortTable(this);return false;">'+txt+'<span class="sortarrow">   </span></a>';
    }
}
 
function ts_getInnerText(el) {
	if (typeof el == "string") return el;
	if (typeof el == "undefined") { return el };
	if (el.innerText) return el.innerText;	//Not needed but it is faster
	var str = "";
 
	var cs = el.childNodes;
	var l = cs.length;
	for (var i = 0; i < l; i++) {
		switch (cs[i].nodeType) {
			case 1: //ELEMENT_NODE
				str += ts_getInnerText(cs[i]);
				break;
			case 3:	//TEXT_NODE
				str += cs[i].nodeValue;
				break;
		}
	}
	return str;
}
 
function ts_resortTable(lnk) {
    // get the span
    var span;
    for (var ci=0;ci<lnk.childNodes.length;ci++) {
        if (lnk.childNodes[ci].tagName && lnk.childNodes[ci].tagName.toLowerCase() == 'span') span = lnk.childNodes[ci];
    }
    var spantext = ts_getInnerText(span);
    var td = lnk.parentNode;
    var column = td.cellIndex;
    var table = getParent(td,'TABLE');
 
	var startline=1;
	if (document.getElementById(table.id+'_tbody')) {
		startline = 0;
		table = document.getElementById(table.id+'_tbody');
	}
 
 
 
    // Work out a type for the column
    if (table.rows.length <= 1) return;
    var itm = ts_getInnerText(table.rows[1].cells[column]);
    sortfn = ts_sort_caseinsensitive;
    if (itm.match(/^\d\d[\/-]\d\d[\/-]\d\d\d\d$/)) sortfn = ts_sort_date;
    if (itm.match(/^\d\d[\/-]\d\d[\/-]\d\d$/)) sortfn = ts_sort_date;
    if (itm.match(/^[£$]/)) sortfn = ts_sort_currency;
    if (itm.match(/^[\d\.]+$/)) sortfn = ts_sort_numeric;
    SORT_COLUMN_INDEX = column;
    var firstRow = new Array();
    var newRows = new Array();
    for (i=0;i<table.rows[0].length;i++) { firstRow[i] = table.rows[0][i]; }
    for (j=startline;j<table.rows.length;j++) { newRows[j-startline] = table.rows[j]; }
 
    newRows.sort(sortfn);
 
    if (span.getAttribute("sortdir") == 'down') {
        ARROW = '  ↑';
        newRows.reverse();
        span.setAttribute('sortdir','up');
    } else {
        ARROW = '  ↓';
        span.setAttribute('sortdir','down');
    }
 
    // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
    // don't do sortbottom rows
    for (i=0;i<newRows.length;i++) { if (!newRows[i].className || (newRows[i].className && (newRows[i].className.indexOf('sortbottom') == -1))) table.tBodies[0].appendChild(newRows[i]);}
    // do sortbottom rows only
    for (i=0;i<newRows.length;i++) { if (newRows[i].className && (newRows[i].className.indexOf('sortbottom') != -1)) table.tBodies[0].appendChild(newRows[i]);}
 
    // Delete any other arrows there may be showing
    var allspans = document.getElementsByTagName("span");
    for (var ci=0;ci<allspans.length;ci++) {
        if (allspans[ci].className == 'sortarrow') {
            if (getParent(allspans[ci],"table") == getParent(lnk,"table")) { // in the same table as us?
                allspans[ci].innerHTML = '   ';
            }
        }
    }
 
    span.innerHTML = ARROW;
}
 
function getParent(el, pTagName) {
	if (el == null) return null;
	else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase())	// Gecko bug, supposed to be uppercase
		return el;
	else
		return getParent(el.parentNode, pTagName);
}
function ts_sort_date(a,b) {
    // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
    if (aa.length == 10) {
        dt1 = aa.substr(6,4)+aa.substr(3,2)+aa.substr(0,2);
    } else {
        yr = aa.substr(6,2);
        if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }
        dt1 = yr+aa.substr(3,2)+aa.substr(0,2);
    }
    if (bb.length == 10) {
        dt2 = bb.substr(6,4)+bb.substr(3,2)+bb.substr(0,2);
    } else {
        yr = bb.substr(6,2);
        if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }
        dt2 = yr+bb.substr(3,2)+bb.substr(0,2);
    }
    if (dt1==dt2) return 0;
    if (dt1<dt2) return -1;
    return 1;
}
 
function ts_sort_currency(a,b) { 
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,'');
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,'');
    return parseFloat(aa) - parseFloat(bb);
}
 
function ts_sort_numeric(a,b) { 
 
    aa = parseFloat(ts_getInnerText(a.cells[SORT_COLUMN_INDEX]));
    if (isNaN(aa)) aa = 0;
    bb = parseFloat(ts_getInnerText(b.cells[SORT_COLUMN_INDEX])); 
    if (isNaN(bb)) bb = 0;
    return aa-bb;
}
 
function ts_sort_caseinsensitive(a,b) {
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).toLowerCase();
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
    if (aa==bb) return 0;
    if (aa<bb) return -1;
    return 1;
}
 
function ts_sort_default(a,b) {
 
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
    if (aa==bb) return 0;
    if (aa<bb) return -1;
    return 1;
}
 
 
function addEvent(elm, evType, fn, useCapture)
// addEvent and removeEvent
// cross-browser event handling for IE5+,  NS6 and Mozilla
// By Scott Andrew
{
  if (elm.addEventListener){
    elm.addEventListener(evType, fn, useCapture);
    return true;
  } else if (elm.attachEvent){
    var r = elm.attachEvent("on"+evType, fn);
    return r;
  } else {
    alert("Handler could not be removed");
  }
}

Utilisation

Il suffit de passer ces 3 étapes toutes simple :

  1. Ajouter le script a votre page. ex :
    html4strict
    <script type="text/javascript" src="scripts/sorttable.js"></script>
  2. Attribuer aux tableau triable la classe sortable. ex :
    html4strict
    <table class="sortable">
  3. Donner un identifiant unique a votre tableau. ex :
    html4strict
    <table class="sortable" id="id_unique">


Et voilà, le tour est joué !

Astuce

Garder une ligne toujours en fin de tableau

Pour qu'une de vos lignes reste toujours en bas de tableau, appliquez lui la classe “sortbottom” :

html4strict
<tr class="sortbottom"><td>q</td><td>17</td></tr>

Avoir les headers fixe (et le contenu qui scroll)

Si vous avez des tableaux qui contiennent un grand nombre de ligne, il peut être judicieux d'avoir une barre de défilement sur votre tableau et surtout de conserver les headers en fixe (comme le fait excel par exemple).
Normalement, la balise tbody sert à ceci. Mais comme internet explorer ne l'entend pas de cette oreille, il faut ruser.
L'idée est d'avoir deux tableau dont l'un des deux est dans un div scrollable :

html4strict
<table class="sortable" id="id_unique">
<tr><td>header 1</td><td>header 2</td></tr>
</table>
<div style="background:#555;overflow:auto;height:150px;">
<table id="id_unique_tbody">
<tr><td>a</td><td>1</td></tr>
<tr><td>b</td><td>2</td></tr>
</table>

On remarque que le tri s'applique sur le 1er tableau (puisqu'il contient la classe “sortable”) cependant, c'est le second tableau qui sera trié.
Si vous faites attention au ID des deux tableau, vous remarquerez que le second à le même ID que le 1er avec juste “_tbody” de plus a la fin.
Lors de l'exécution du tri, la fonction va regarder s'il existe un tableau qui a ID = ID + “_tbody”. Si c'est le cas, le tri s'effectue sur celui-ci et non plus sur le 1er.
Voici un exemple fonctionnel : sorttable_exemple.zip

La difficulté de cette méthode réside dans le fait d'avoir exactement la même largeur entre les colonnes du tableau 1 et du tableau 2.
pour cela, je vous conseille d'appliquer un CSS de ce goût :
css
#id_unique_tbody,#id_unique {
	width:300px;
}
#id_unique_tbody tr td,#id_unique tr td {
	width:50%;
	border:1px solid #987;
}

Rendre un peu plus joli

Vous pouvez rendre l'entête triable un peu plus jolie, pour ça, appliquer un style css dans les classes : table.sortable a.sortheader et table.sortable span.sortarrow.
exemple :

css
table.sortable a.sortheader {
    background-color:#eee;
    color:#666666;
    font-weight: bold;
    text-decoration: none;
    display: block;
}
table.sortable span.sortarrow {
    color: black;
    text-decoration: none;
}

Source

Adapté de : http://www.kryogenix.org/code/browser/sorttable/


iDo 12/12/2005 11:50