Note: この記事は、3年以上前に書かれています。Webの進化は速い!情報の正確性は自己責任で判断してください。
| 機種 | 登録数 |
|---|---|
| iPhone | 8 |
| WS009KE | 1 |
| WS018KE | 1 |
| WX310K | 1 |
| WX320T | 1 |
| WX331K | 2 |
| 705N | 5 |
| 705NK | 39 |
恒例のライブラリを使わないシリーズ。今回は、テーブル行を並べ替えるスクリプトを書いてみました。実は前にも作ったことはあるんだけど、当時はてきとーにその辺から拾ったものを継ぎ合わせただけで理解してたわけではなく... まあ気にはなってたので、いま作ってみたらどうなるだろう、と。
結果的に、JavaScriptの連想配列をソートする方法を学びました。これがあるから自作はやめられない。車輪の再発明は勉強にはは必要よ。性能面でも、とりあえず400行くらいのデータテーブルなら(画像入ってても)特に遅くはないようです。仕事で使ったから大丈夫。
以下、解説です。
HTML
<table border="1" cellspacing="0" cellpadding="0" id="device"><thead><tr><th><a href="" onclick="table_sort.exec('device',0,'str');return false;">機種</a></th><th><a href="" onclick="table_sort.exec('device',1,'num');return false;">登録数</a></th></tr></thead><tbody><tr><td>iPhone</td><td>8</td></tr><tr>...goodness...<tbody></table>
<th>にリンクを置いて、onclickで起動させています。「table_sort.exec('device',0,'str');」がその部分。第一引数にテーブルのID名、第二引数に対象列の位置(左から0,1,2...)、第三引数で「文字列としてソート」か「数値としてソート」かを指定しています。第三引数が「str」以外であれば数値としてソートします。
<tbody>の中身がソート対象になります。今回のスクリプトでは並べ替えたリストと<tbody>の中身をそっくり入れ替えているので、使用する場合は必ずテーブル内に<tbody>を含めるよう、注意してください。
ちなみにJavaScript無効状態を考慮して、起動の<a>要素自体をonloadで生成するカスタムも可能ですが、今回は解りやすさを考慮してこのような形とします。あしからず。
JavaScript
var table_sort = {exec: function(tid,idx,type){var table = document.getElementById(tid);var tbody = table.getElementsByTagName('tbody')[0];var rows = tbody.getElementsByTagName('tr');var sbody = document.createElement('tbody');//save arrayvar srows = new Array();for(var i=0;i<rows.length;i++){srows.push({row: rows[i],cel: rows[i].getElementsByTagName('td')[idx].innerHTML,idx: i});}//sort arraysrows.sort(function(a,b){if(type == 'str')return a.cel < b.cel ? 1 : -1;elsereturn b.cel - a.cel;});if(this.flag == 1) srows.reverse();//replacefor(var i=0;i<srows.length;i++){sbody.appendChild(srows[i].row)}table.replaceChild(sbody,tbody);this.replaceText(table,idx);//set flagthis.flag = this.flag > 0 ? 0 : 1;},replaceText: function(table,idx){var thead = table.getElementsByTagName('a');//preset header-textif(!this.exp){this.text = new Array();for(var i=0;i<thead.length;i++){this.text.push(thead[i].firstChild.nodeValue);}this.exp = 1;}//set&remove suffixfor(var i=0;i<thead.length;i++){if(i == idx){thead[i].firstChild.nodeValue = this.flag == 0? this.text[i] + this.suffix[0]: this.text[i] + this.suffix[1];}else {thead[i].firstChild.nodeValue = this.text[i];}}},suffix: ['▽','△'],flag: 0}
table_sort.ececがソートを実行するメソッド。クリック時のソート順(昇順/降順)を表示する表示切替がtable_sort.replaceText。その際の識別記号をtable_sort.suffixに格納してます。で、table_sort.flagで現在が昇順か降順か(次のソートが降順か昇順か)を保存しておきます。
処理の流れとしては、いったん<tbody>の中身を保存しておき(8~16行目)、<td>の中身で並べ替え(18~25行目)、得られたソート結果と既存の行を入れ替える(31行目)。配列のソートにはsortメソッドが使えるけど、getElementsByTagNameで取得できるノードリストは配列ではないため、行の内容を保存した連想配列をsortする方法を採ります。
けっこうな分量を割いているtable_sort.replaceTextメソッドは、クリックしたときの昇順/降順を判りやすくするためにリンクテキストをちょちょーいと弄っているだけなので、別に無くてもソート自体は動きます。お好みでカスタムすると良いでしょう。接尾辞(この例では△/▽)を換えたいだけなら、table_sort.suffixを編集するだけでOKです。
比較関数
JavaScriptのArray.sort()メソッドには、引数に比較関数を指定できます。引数なしだとアルファベット順でソートします。つまりbはaより大きく、9は80より大きく、12は11より大きいといった具合です。
引数に比較関数を指定すれば、より柔軟なソートが可能になります。比較関数は引数を2つ取り、両者の比較します。たとえば比較関数compFunc(a,b)の返り値が、0未満であればaをbより小さい添字とし、0より大きければbをaより小さい添字とします。ちなみに0を返す場合の順序関係は無いものとして処理されますが、この動作は保証されていないようです。。
基本的には、ある順序の基準において a が b より小さい場合には -1 を、大きい場合には1を返すよう、いろいろ工夫してやることになります。単純に数値として比較するなら、単に引いてやれば良いでしょう。幾つかサンプルを提示してみます。
//数値としてソートarray.sort(function(a,b){ return a - b });//日付でソートarray.sort(function(a,b){ return a.date - b.date });//文字数で比較array.sort(function(a,b){ return a.length - b.length });//ランダムにソートarray.sort(function(){ return Math.random() - Math.random() });
いろいろ弄ってみると楽しいかもね。ちなみに比較関数はソートが完了するまで何度も呼び出されているようです。なので軽いにこしたことはない。
Note: スパム対策が面倒なので、コメント投稿を廃止しました。以前のコメントは残します。
ご意見・ご要望はtwitter@sigwygかはてブコメントにて。