Webページのテーブルにソート機能が欲しいというのはよくある話で、JQueryあたりのライブラリを探して組み込むのが一般的だと思う。
ただ、組み込み先のページが10年以上前のprototype.jsを駆使した画面でJQueryと折り合いを付けるのが大変だったり、バンドルされたCSSの調整が面倒、といった問題が付きまとう。
そこで高速、かつ軽量で有名なフレームワークVanilla jsでの実装を試みた。
#仕様
-
<table>
が<thead>
と<tbody>
で構成されていることが条件 - ページ内全ての上記条件に当てはまる
<table>
をソート対象にする - ヘッダー行のカラム
table > thead > th
をクリックすると対象列の昇順でソートする - もう一回クリックすると降順でソートし、昇順、降順をクリックする度に切り替える
- 数値変換可能文字列は数値として評価、その他は文字コードで評価しソートする
#コード
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title></title>
</head>
<body>
<table>
<thead>
<th>key</th>
<th>value</th>
</thead>
<tbody>
<tr>
<td>5</td>
<td>aaa</td>
</tr>
<tr>
<td>1</td>
<td>bbb</td>
</tr>
<tr>
<td>4</td>
<td>ccc</td>
</tr>
<tr>
<td>2</td>
<td>ddd</td>
</tr>
<tr>
<td>3</td>
<td>eee</td>
</tr>
</tbody>
</table>
<script type="text/javascript">
(function () {
function sort(tbody, compareFunction) {
var rows = tbody.children;
if(!rows || !rows[0] || rows.length == 1) return;
var size = rows.length;
var arr = [];
for(var i = 0; i < size; i++) arr.push(rows[i]);
arr.sort(compareFunction);
for(var i = size - 1; i > 0; i--) tbody.insertBefore(arr[i-1], arr[i]);
}
function numConvert(s) {
return s == Number(s) ? Number(s) : s;
}
function asc(idx) {
return function(a, b) {
var a_ = numConvert(a.children[idx].innerText);
var b_ = numConvert(b.children[idx].innerText);
return a_ > b_ ? 1 : -1;
};
}
function desc(idx) {
return function(a, b) {
var a_ = numConvert(a.children[idx].innerText);
var b_ = numConvert(b.children[idx].innerText);
return a_ < b_ ? 1 : -1;
};
}
function sortEvent(tbody, idx) {
var mode = true;
return function(e) {
if(mode) sort(tbody, asc(idx));
else sort(tbody, desc(idx));
mode = !mode;
};
}
var ts = document.getElementsByTagName('table');
for(var i = ts.length; i--; ) {
var ths = ts[i].tHead.getElementsByTagName('th');
for(var j = ths.length; j--; )
ths[j].addEventListener("click", sortEvent(ts[i].tBodies[0], j));
}
})();
</script>
</body>
</html>
#動作確認
せっかくなのでQiitaページのテーブルをソートしてみた
やり方はギャル文字変換の記事を参照
OK!
#所感
ブラウザ互換の問題がなくなりつつある今、Vanillaの選択肢はアリな気がしてきた
#おまけ ES6版
書いてみたけどIEで動かないからお蔵入り
個人的にはブロックスコープとconst、letがかなり好き
アローはなくてもいいかな
{
const
sort = (tbody, compareFunction) => {
const rows = tbody.children;
if(!rows || !rows[0] || rows.length == 1) return;
const size = rows.length;
const arr = [];
for(let row of rows) arr.push(row);
arr.sort(compareFunction);
for(let i = size - 1; i > 0; i--) tbody.insertBefore(arr[i-1], arr[i]);
},
numConvert = (s) => (s == Number(s)) ? Number(s) : s,
getValue = (tr, idx) => tr.children[idx].innerText,
asc = (idx) => (a, b) => (numConvert(getValue(a, idx)) > numConvert(getValue(b, idx))) ? 1 : -1,
desc = (idx) => (a, b) => (numConvert(getValue(a, idx)) < numConvert(getValue(b, idx))) ? 1 : -1,
sortEvent = (tbody, idx) => {
let mode = true;
return (e) => {
mode ? sort(tbody, asc(idx)) : sort(tbody, desc(idx));
mode = !mode;
}
}
for(let table of document.getElementsByTagName('table')) {
const ths = table.tHead.getElementsByTagName('th');
for(let j = ths.length; j--; )
ths[j].addEventListener("click", sortEvent(table.tBodies[0], j));
}
}