uhyohyo.net

JavaScript初級者から中級者になろう

二章第八回 テーブルの操作

このページの最終更新日:

テーブルとは、こういうやつのことです。


<table>
  <tr>
    <td>test1</td>
    <td>test2</td>
  </tr>
  <tr>
    <td>test3</td>
    <td>test4</td>
  </tr>
</table>

テーブルとはすなわちです。このHTMLは次のような表を表しています。

test1test2
test3test4

テーブルの木構造

HTMLを見る限り、上のテーブルの木構造は次のようだと考えられます。ただし、タグとタグの間の改行によって生じるテキストノードは省略しています。

table
  • tr
    • td
      • #text
    • td
      • #text
  • tr
    • td
      • #text
    • td
      • #text

しかし、実際は違います。実は、実際には上のHTMLからは以下のような木構造が生成されます。

table
  • tbody
    • tr
      • td
        • #text
      • td
        • #text
    • tr
      • td
        • #text
      • td
        • #text

table直下にあるのはtbodyという要素で、trはその下にあります。

詳しくはHTMLを勉強してほしいのですが、tableの中にはtbodyという要素があり、trはその中に入れます。このtbodyという要素は省略可能であり、まったく書かなくても暗黙のうちに存在すると見なされるのです。

他にもtheadやtfootという要素も、tbodyと並んでtableの中に存在する可能性があり、それらの中にもtrが存在する可能性があります。(thead, tfootについては次回詳しく紹介します。)

昔のHTMLではtableの直下にtrを書くことは認められておらず、上のように書いたら必ず暗黙のtbodyが補完されていました。

最近のHTMLでは、tbodyを書く代わりにtrを直にtableの中に書くことも実は認められていますが、実際のブラウザは後方互換性の観点からtbodyを補完するものが多いようです。

これらの要素のせいで、テーブルの中ではtr要素がいろいろな場所に分散してしまう可能性があり、前回までに紹介した木構造の操作によってテーブルの行を一元的に扱うのは難しいです。そこで、テーブルの操作のための特別なプロパティやメソッドが用意されています。

HTMLTableElement

table要素のHTMLElementは、HTMLTableElementと呼ばれます。これは、いくつもの独自のプロパティやメソッドを持っています。

rows

そのうちの1つが、rowsプロパティです。これは、そのテーブルが持つtr要素全てがまとめて集まっています。これを通してテーブルのtr要素を一元的に操作できます。

要素がいくつも集まっているということからNodeListが思い浮かぶと思いますが、このオブジェクトはHTMLCollectionといいます。(細かい違いとか歴史的経緯がありますが、)これはNodeListと同じように扱えます。

例えば、

<!doctype html>
<html>
  <head>
    <title>test</title>
  </head>
  <body>
    <table id="aaaaa">
      <tr>
        <td>test1</td>
        <td>test2</td>
      </tr>
      <tr>
        <td>test3</td>
        <td>test4</td>
      </tr>
    </table>

    <script type="text/javascript">
      var table = document.getElementById('aaaaa');

      var collection = table.rows;

      console.log(collection[0]);
    </script>
  </body>
</html>

この場合、いつものようにgetElementByIdでtable要素を取得し、rowsを変数collectionに代入し、その0番目のノードのtagNameを表示しています。rowsはtr要素の集まりなので、当然tr要素が表示されます。実はHTMLCollectionの中の要素の順番はページ内の順番と一致している(ソースの中で先に書かれたものが先に来る)ので、表示されるtr要素は次のものです。


<tr>
  <td>test1</td>
  <td>test2</td>
</tr>

こうして、テーブルのうちのtr要素を取得する方法がわかりました。たとえば、テーブルの中のtr要素を全て処理するようなプログラムも書けることでしょう。

HTMLTableRowElement

tr要素のHTMLElementは、HTMLTableRowElementといいます。

これもいろいろなプロパティやメソッドを持っています。

cells

cellsプロパティは、rowsプロパティのtd版という感じです。そのtr要素が持つtd要素(とth要素)のHTMLCollectionです。

ちなみに、入っている順は、先ほどと同様にページ内の順番(正確にはtree orderといいます)なので、つまり単に左順番に入っています。一番左が0番目、次が1番目・・・のようになっています。

<!doctype html>
<html>
  <head>
    <title>test</title>
  </head>
  <body>
    <table id="aaaaa">
      <tr>
        <td>test1</td>
        <td>test2</td>
      </tr>
      <tr>
        <td>test3</td>
        <td>test4</td>
      </tr>
    </table>

    <script type="text/javascript">
      var table = document.getElementById('aaaaa');

      var tr = table.rows[0];
      var td = tr.cells[0];

      td.firstChild.nodeValue = "tttttttt";
    </script>
  </body>
</html>

左上のtd要素の中身が"tttttttt"に変わりました。

0番目のtr要素を変数trに代入し、その0番目のtd要素を変数tdに代入しています。

tdのfirstChildはテキストノードなので、そのnodeValueを変更することで左上のtd要素が変更されました。

テーブルの操作

さて、これで、テーブルの行やセル(1つ1つのtdやth要素のこと)をたどって、末端のテキストノードくらいは操作できるようになりました。

次に、行を増やしたり、セルを増やしたりといったテーブル自体の操作について解説していきます。

列・セルの追加

<!doctype html>
<html>
  <head>
    <title>test</title>
  </head>
  <body>
    <table id="aaaaa">
      <tr>
        <td>test1</td>
        <td>test2</td>
      </tr>
      <tr>
        <td>test3</td>
        <td>test4</td>
      </tr>
    </table>

    <script type="text/javascript">
      var table = document.getElementById('aaaaa');

      var newtr = table.insertRow( table.rows.length );

      for(var i=0;i<2;i++){
        var newtd = newtr.insertCell( newtr.cells.length );
        newtd.appendChild( document.createTextNode('testtest'+i) );
      }
    </script>
  </body>
</html>

insertRow,insertCellという2つの新しいメソッドが登場しています。insertRowはtable要素のHTMLTableElementが、insertCellはtr要素のHTMLTableRowElementが持つメソッドだということがわかります。

insertRowは、テーブルに新しい列(tr要素)を追加して、そのtr要素を返すメソッドです。つまり、

table
  • tbody
    • tr
      • td
        • #text
      • td
        • #text
    • tr
      • td
        • #text
      • td
        • #text
    • tr    ← これ(新しく追加されたtr)

を返すということになります。

createElementやappendChildなどをしなくても、insertRowを行った時点でtr要素が新しく作成されて、追加するところまでやってくれるわけですね。もちろん、このtr要素に子を追加すれば、ページの木構造にそれは追加されることになり、ページに反映されます。

insertRowは好きな場所に列を挿入することができます。入れる場所は、引数で数値で指定します。

指定のしかたは、その要素が「何番目になるか」というふうに指定します。たとえば0とすると、挿入した列が0番目になるということなので、一番最初に挿入されます。今回は、引数はtable.rows.lengthです。table.rowsはtable要素が持つtr要素のリストなので、これはtr要素の数です。trはいま0番目から(length-1)番目まであるので、length番目に追加すると一番最後になるというわけです。

実は、これはtable.insertRow(-1)としても構いません。つまり、番号に-1を指定すると最後に追加してくれるようになっています。さらに、table.insertRow()のように引数を省略してもよく、この場合も最後に追加されます。ただし、引数を省略できる機能は比較的新しい機能なので、古いブラウザだとこれは動作しない危険性があり、-1を渡すほうが安全です(2017年6月現在)

さて、こうしてできたtr要素は、その下にtd要素などは持っていません。そこで、次にtr要素にセルを追加します。

それは最後のfor文が行っています。このfor文は同じ処理を2回繰り返しています。iが0,iが1のときに処理が行われ、iが2になると終了します。

これは、同じような処理を2回書くのが面倒なので、for文で繰り返すようにしたというだけです。

中で使われている新しいメソッドはinsertCellです。これはHTMLTableRowElementが持つメソッドで、tr要素にtd要素を追加して、それを返すというものです。使い方はinsertRowとよく似ています。引数に-1を指定したり引数を省略したりできるのも同じです。

つまり、newtdには新しくできた

table
  • tbody
    • tr
      • td
        • #text
      • td
        • #text
    • tr
      • td
        • #text
      • td
        • #text
    • tr
      • td    ← これ

が代入されるということになります。

これも、insertRowと同じく空なので、中身のテキストノードを追加する必要があります。その処理が次の行です。td要素にappendChildで、createTextNodeで作ったテキストノードが追加されています。中身であるcreateTextNodeの引数は、「'testtest'+i」で、この変数iはループのカウンタだから、1回目(iが0)は'testtest0'、2回目(iが1)は'testtest1'となります。

つまり、最終的な木構造は次のようになります。

table
  • tbody
    • tr
      • td
        • #text
      • td
        • #text
    • tr
      • td
        • #text
      • td
        • #text
    • tr
      • td
        • #text "testtest0"
      • td
        • #text "testtest1"

画面の結果を見るとこのとおりになっているのがわかります。

列・セルの除去

逆に、列やセルを除去する方法もあります。

<!doctype html>
<html>
  <head>
    <title>test</title>
  </head>
  <body>
    <table id="aaaaa">
      <tr>
        <td>test1</td>
        <td>test2</td>
      </tr>
      <tr>
        <td>test3</td>
        <td>test4</td>
      </tr>
    </table>

    <script type="text/javascript">
      var table = document.getElementById('aaaaa');

      table.deleteRow(0);
    </script>
  </body>
</html>
      

「test1」「test2」の行が消えてしまいました。

これは、2行しかありませんね。1行目はtable要素の取得で、2行目でdeleteRowというメソッドを呼び出しています。

引数は数字ですね。このメソッドは、(引数)番目の行を除去するというものです。

今回は引数が0なので、0番目の行、つまり最初の行が消えたというわけです。removeChildなどと同じように、子ノードがあってもまとめて除去されます。

列の番号

removeChildでは引数として除去したいノードそのものを渡したのに、insertRowやdeleteRowでは番号を引数として渡しています。これは、たまに、特にdeleteRowで不都合となることがあります。先に除去したいtr要素のHTMLElementが手に入った場合など、どうするのでしょう。

実は、それを解決してくれるrowIndexというプロパティをtr要素は持っています。ずばり、テーブルの中でそのtrが何番目かを表します。一番最初は例のごとく0番目です。これを使えば、簡単に何番目か知ることができます。

セルの除去

最後に、セルも同じように削除することができます。tr要素のオブジェクトが持つdeleteCellを使います。deleteRowと同じように番号を引数に渡します。

ちなみに、tr要素ではrowIndexだった自身の番号は、td,th要素ではcellIndexにあたります。