事の発端
https://github.com/javve/list.js/issues/623
#問題点
List.jsを普通に使った場合、ソートしてもちゃんと並ばないじゃん、というやつですね。よくある問題です。表示用の文字とソート用の値を使いわけないとですね。
#解決策
解決する方法は2つあります。手っ取り早く済ませる方法と、ちゃんとソート関数作る方法。
##解決策1. data-xxxを使ってソート用の値を埋め込む
簡単に済ませる方法は、ソート用の値をちゃんと作ることです。目立たないですけど本家にサンプルもあります。
http://listjs.com/docs/
Example 6: Using data attributes and other custom attributes (introduced in v1.2.0)
以下の例では、id_percのTDにdata-perc属性を追加しました。data属性に追加する値は、ソートしやすいように桁合わせ、0埋め、小数点処理、不要な文字の削除($マーク、¥マーク、カンマ)などを事前に済ませておいてください。当然ですが、HTMLを出力する側のプログラムの修正が必要ですね。
<table id="tableid" class="table-list" data-currentpage="1">
<thead>
<tr>
<th><button type="button" class="sort" data-sort="id_name">Parish Name <i class="caret"></i></button></th>
<th style="width: 140px;"><button type="button" class="sort" data-sort="id_town">Town <i class="caret"></i></button></th>
<th style="width: 80px;"><button type="button" class="sort" data-sort="id_give">Givers <i class="caret"></i></button></th>
<th style="width: 120px;"><button type="button" class="sort" data-sort="id_comm">Commitment <i class="caret"></i></button></th>
<th style="width: 120px;"><button type="button" class="sort" data-sort="id_paid">Paid to Date <i class="caret"></i></button></th>
<th style="width: 120px;"><button type="button" class="sort" data-sort="id_goal">Goal <i class="caret"></i></button></th>
<th style="width: 100px;"><button type="button" class="sort" data-sort="id_perc">Percent <i class="caret"></i></button></th>
</tr>
</thead>
<tbody class="list">
<tr>
<td class='id_name'>002, Blessed Sacrament </td>
<td class='id_town'>Rochester </td>
<td class='id_give'>107</td>
<td class='id_comm'>$35,862.00</td>
<td class='id_paid'>$34,904.00</td>
<td class='id_goal'>$73,822.00</td>
<td class='id_perc' data-perc='0.4858'>48.58%</td> <!--★ここ-->
</tr>
さらに、scriptでList.jsのインスタンスを作るときに、id_percに対して一工夫します。
いつもはvalueNamesの配列にカラム名だけを渡していますが、これは、<td></td>や<span></span>なとのタグで挟まれたテキストノードの値をソートに使います。
{}
を使ってname
とattr
の2つの情報を渡します。これをやることで、id_percでソートするときは、data-percの値を使うのね、ということが伝わります。<a>タグのhrefや<img>タグのsrcで並び替えたいときも、この方法を使います。
<script>
var options = {
valueNames: [ 'id_name', 'id_town', 'id_give', 'id_comm', 'id_paid', 'id_goal',
//'id_perc'
{ name: 'id_perc', attr: 'data-perc' },
]
};
var tableList = new List('tableID', options);
</script>
これで、小数も%もキレイに並ぶと思います。
解決策2. ソート関数を再定義する
List.jsのsortfunctionの仕組みを使って、複数カラムのソートを実現する で調べましたが、List.jsのインスタンスはsort用の関数を持てるようになっていて、デフォルトではnaturalSort
という関数が仕込まれています。これをハックしてやります。
ソートする関数で解決するので、HTMLの中にソート用の値と表示用の値のデータを持たなくて済みます。
変更点は、
- sortイベントで呼び出される関数を別のものを指定(my_sort)を指定してインスタンス作成をし、
- my_sortではカラム名を見分けて呼び出す関数を変えるようにし、
- my_numbersort関数は、要らない文字列を削り、小数に変換してソートする
<script>
var options = {
valueNames: [ 'id_name', 'id_town', 'id_give', 'id_comm', 'id_paid', 'id_goal',
{ name: 'id_perc', attr: 'data-perc' },
],
//sortFunctionで自作関数を呼ぶように指定
sortFunction: my_sort
};
var tableList = new List('tableID', options);
//List.jsをsortで呼ばれる関数
function my_sort(itemA, itemB, options){
sort_column = options.valueName;
v1 = itemA.values()[sort_column];
v2 = itemB.values()[sort_column];
//console.log(options);
//console.count("sort count");
//ソートに使う関数を決める。とりあえずカラム名べた書き。
if( sort_column == 'id_name' || sort_column =='id_town'|| sort_column =='id_perc'){
return my_naturalsort(v1,v2);
}else{
return my_numbersort(v1,v2);
}
}
//List.jsのソートを呼ぶだけの関数
function my_naturalsort(a,b){
return tableList.utils.naturalSort(a,b)
}
//文字列処理をしてからList.jsのソートを呼ぶ関数
function my_numbersort(a,b){
console.log('A=%s, B=%s, result=%s', a,b, my_naturalsort(a,b));
s1 = a.replace( '$', '' ).replace(',','');
s2 = b.replace( '$', '' ).replace(',','');
console.log('A=%s, B=%s, result=%s ', s1,s2, my_naturalsort(s1,s2));
f1 = parseFloat(s1);
f2 = parseFloat(s2);
console.log('A=%s, B=%s', f1,f2, my_naturalsort(f1,f2));
return my_naturalsort(f1, f2);
}
</script>
ソートしやすいような値をHTMLに仕込むほうがいいか、ソート関数が呼ばれるたびに文字列変換して並び替えるのがいいのかは、各自判断ください。
#おまけ
ソート指定したカラムの値が同じときに、パーセントの降順で並ぶサンプルを作りました。参考にどうぞ。
https://github.com/kanaxx/listjs_sample/blob/master/numbersort.html