LoginSignup
28
30

More than 5 years have passed since last update.

Vanilla JSによる軽量tableソート

Last updated at Posted at 2016-08-26

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ページのテーブルをソートしてみた
やり方はギャル文字変換の記事を参照

ソート前
image

ソート後(key2)
image

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));
  }
}
28
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
30