TITLE:JavaScriptでHtmlのtable要素をCSVに変換する

Posted by aterai at 2012-07-25

JavaScriptでHtmlのtable要素をCSVに変換する

概要

Html の table 要素を CSV に変換する Bookmarklet です。colspan、rowspan がある場合は、同文字列を挿入しています。

  • ブックマークレットなどに登録して実行
  • table要素をダブルクリックすると、tableがtextareaに置換されて、内容がCSVになりコピー可能
    • 子要素にtableが存在する場合は、ダブルクリックに反応しない
  • textareaをダブルクリックすると、元のtableに戻る
  • <thead><tfoot>は気にしていない(例えば、tbodyの前にあるtfootが、そのまま先の行としてCSVになる)
  • <capthion>は無視
  • <colgroup>のspanには未対応

ソースコード

(function() {
  var listener = function(e) {
    var tx = document.createElement("textarea"),
        tr = this.getElementsByTagName("tr"),
        csv = "",
        i,j,k,l,xoff, text, csvRow, cells, td, array = [], lenr = tr.length, lenc;

    for(i=0; i<lenr; i++) {
      if(array[i] == null) array[i] = []; //rowspanですでに初期化されている場合があるので
      csvRow = "";
      cells = tr.item(i).cells;
      lenc = cells.length;
      for (j=0; j<lenc; j++) {
        td = cells.item(j);
        //タブの置換、両側trim、ダブルクオートの二重化
        //text = td.innerText.replace(/\t/g," ").replace(/(^\s+)|(\s+$)/g, "").replace(/\"/, "\"\"");
        text = td.innerHTML.replace(/<.*?>/mg, "").replace(/\t/g," ").replace(/(^\s+)|(\s+$)/g, "").replace(/\"/, '""');
        //前の行のrowspanですでにこのセルが使用されている場合はxoffだけ移動
        xoff = 0;
        while(array[i][j+xoff] != null) {
          xoff++;
        }
        for(k=0; k<td.colSpan; k++) {
          array[i][j+xoff+k] = text;
          for(l=0; l<td.rowSpan; l++) {
            if(array[i+l] == null) array[i+l] = [];
            array[i+l][j+xoff+k] = text;
          }
        }
      }
    }
    lenr = array.length;
    lenc = array[0].length;
    for(k=0; k<lenr; k++) {
      csvRow = "";
      for(l=0; l<lenc; l++) {
        csvRow += "\t\""+array[k][l]+"\"";
      }
      if (csvRow != "") {
        csvRow = csvRow.substring(1,csvRow.length);
      }
      csv += csvRow+"\n";
    }
    tx.value = csv;
    //textareaのサイズは適当
    tx.style.width  = '80%';
    tx.style.height = "240px";
    tx.originalTable  = this;
    this.tx = tx;
    this.parentNode.replaceChild(this.tx, this);

    tx.addEventListener("dblclick", function(e) {
      this.parentNode.replaceChild(this.originalTable, this);
    }, false);
  },
  table = document.getElementsByTagName("table"), i = 0, len = table.length;
  for(; i<len; i++) {
    //tableがネストしている(子tableが存在する)場合は、クリックしても無視する
    if(table[i].getElementsByTagName("table").length>0) continue;
    table[i].addEventListener("dblclick", listener, false);
  }
}());

Bookmarklet

  • 名前
    • table2csv
  • アドレス
    javascript:(function(){var d=function(t){var q=document.createElement("textarea"),v=this.getElementsByTagName("tr"),o="",p,n,m,h,x,w,u,y,g,s=[],r=v.length,f;for(p=0;p<r;p++){if(s[p]==null){s[p]=[]}y=v.item(p).cells;f=y.length;for(n=0;n<f;n++){g=y.item(n);w=g.innerHTML.replace(/<.*?>/mg,"").replace(/\t/g," ").replace(/(^\s+)|(\s+$)/g,"").replace(/\"/,'""');x=0;while(s[p][n+x]!=null){x++}for(m=0;m<g.colSpan;m++){s[p][n+x+m]=w;for(h=0;h<g.rowSpan;h++){if(s[p+h]==null){s[p+h]=[]}s[p+h][n+x+m]=w}}}}r=s.length;f=s[0].length;for(m=0;m<r;m++){u="";for(h=0;h<f;h++){u+='\t"'+s[m][h]+'"'}if(u!=""){u=u.substring(1,u.length)}o+=u+"\n"}q.value=o;q.style.width="80%";q.style.height="240px";q.originalTable=this;this.tx=q;this.parentNode.replaceChild(this.tx,this);q.addEventListener("dblclick",function(i){this.parentNode.replaceChild(this.originalTable,this)},false)},c=document.getElementsByTagName("table"),b=0,a=c.length;for(;b<a;b++){if(c[b].getElementsByTagName("table").length>0){continue}c[b].addEventListener("dblclick",d,false)}}());
    • YUI Compressor で圧縮
  • アドレス(テスト用)
    javascript:(function(){var s=document.createElement("script");s.charset="UTF-8";s.src="http://terai.xrea.jp/data/javascript/table2csv.js?"+(new Date()).getTime();document.body.appendChild(s)}());

テスト用table

  • rowspan, colspan
    ABCD
    123
    456
    7
  • footer
    ファイル数サイズ総行数コメント行実行数空行数コメント率
    footer
    合計1695291916174370770458480412521
    平均3131310314555017444.13%

参考リンク

コメント

  • もっと良い物(rowspan, colspan に対応している)が、すでに沢山あるはずだと思うけど、検索しても見つからない…ので、作ってみた。自分に必要だった作業にはこれで十分だったけど、もうすこし修正した方がいいかもしれない…。 -- aterai
  • FireFoxでも動くようにinnerTextを使用しないように修正。 -- aterai