Help us understand the problem. What is going on with this article?

HTML5のtableタグをCSV出力(ダウンロード)してみる

More than 1 year has passed since last update.

はじめに

・今回は友人に頼まれ、WEBブラウザ(HTML)で表示されている表(tableタグ)をCSV出力させるHTML & java scriptのコードを書いてみました。あんまり需要があるかわかりませんが、優しい目で見ていただけると幸いです。
・WEBサーバを立てるのは面倒だったのでクライアント側(WEBブラウザ側)のみで処理させています。Chrome、IE、Firefoxにて動作を確認しています。
・ほぼ個人用のメモ&日記的なものですが、よろしければご参照ださい。

完成物

・入力フィールドの「店舗コード」、「品目」、「金額」に文字、数字を入力し、行を追加ボタンを押すと表に行が追加されます。「CSVファイルダウンロード」のリンクをクリックすると現時点で表示されている表をCSV形式でダウンロードされます。
Screenshot 2018-05-01 at 12.29.54.png

コード全文

・本来は、CSSとjava scriptは別ファイルに書いておくべきですが、都合上htmlに直書きしました。
・半分以上がCSS(HTMLの見てくれ)ですので、本投稿では、後半のbody属性の中身について解説していきます。
・以下コードは丸コピしてhtmlファイルで保存後、ブラウザで開けば使える状態になっています。

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <style type="text/css">
        @charset "UTF-8";
        /* ========BASIC======== */
        html {
        overflow-y:scroll;
        }

        body {
        margin:0;
        padding:0;
        line-height:1.6;
        letter-spacing:1px;
        font-family:Verdana, Helvetica, sans-serif;
        font-size:12px;
        color:#333;
        background:#fff;
        }

        br {
        letter-spacing:normal;
        }

        a {
        color:#0089a1;
        text-decoration:none;
        }

        a:hover {
        color:#0089a1;
        text-decoration:underline;
        }

        img {
        border:0;
        vertical-align:bottom;
        }

        h1,h2,h3,h4,h5,h6 {
        margin:0;
        }


        /* ========TEMPLATE LAYOUT======== */
        #top {
        width:780px;
        margin:10px auto;
        border:1px solid #333;
        }

        #header {
        width:780px;
        }

        #contents {
        clear:both;
        }

        #main {
        float:left;
        width:540px;
        padding:10px;
        }


        /* ========HEADER CUSTOMIZE======== */
        #header h1 {
        margin:0;
        padding:10px;
        font-size:24px;
        }

        #header h1 a {
        color:#333;
        }


        /* ========MAIN CONTENTS CUSTOMIZE======== */
        #main h2 {
        margin-bottom:5px;
        padding:5px 0;
        font-size:16px;
        border-bottom:3px double #ccc;
        }

        #main h3 {
        margin-bottom:5px;
        padding:5px;
        font-size:14px;
        border-left:5px solid #0089a1;
        border-bottom:1px dotted #ccc;
        }

        #main h4 {
        margin-bottom:5px;
        padding:5px;
        font-size:13px;
        color:#fff;
        background:#0089a1;
        }

        #main h5 {
        margin-bottom:5px;
        font-size:13px;
        border-bottom:1px dotted #ccc;
        }

        #main h6 {
        margin-bottom:5px;
        font-size:13px;
        }


        #main p {
        margin:0 0 1em 0;
        }


        /* INFORMATION CUSTOMIZE */
        * html body #main dl.information dd div {
        display:inline-block;
        }

        #main table {
        width:100%;
        border-collapse:collapse;
        }

        #main table th {
        padding:5px;
        font-size:12px;
        text-align:left;
        border:1px solid #aaa;
        background:#f0f7fc;
        }

        #main table td {
        padding:5px;
        font-size:12px;
        text-align:left;
        border:1px solid #aaa;
        }
    </style>

    <title>デモ用アプリ</title>
    </head>
    <body>
        <div id="top">
            <div id="header">
                <h1>hogehoge株式会社</h1>
            </div>
        <div id="contents">
            <div id="main" style="width: 760px;">
              <h2>デモ用アプリ</h2>
              <p>ここにこのサンプルコードの説明を記載してださい。</p>
              <h4>店舗売上データ一覧</h4>
                <table id="table1"  border="1" cellpadding="10">
                    <tr>
                        <th>店舗コード</th>
                        <th>品目</th>
                        <th>金額(円)</th>
                    </tr>
                </table>
                <div style="padding-top: 10px;">
                    <h5>↓入力フィールド</h5>
                    <input name="table1_cell_value" id="th_value1" type="text" placeholder="店舗コード">
                    <input name="table1_cell_value" id="th_value2" type="text" placeholder="品目">
                    <input name="table1_cell_value" id="th_value3" type="text" placeholder="金額(円)">
                    <input type="button" value="行を追加" onclick="add_line()">
                    <b><a id="download" href="#" download="test.csv" onclick="handleDownload()">csvファイルダウンロード</a></b>
                </div>
            </div>
        </div>

  <script>
    //ここから表の行追加のコード
    function add_line() {
        for(var i = 0; i < document.getElementsByName("table1_cell_value").length; i++){
            if(document.getElementsByName("table1_cell_value")[i].value ==""){
                alert("未入力項目があります。");
                return false;
            }
        }

        var table = document.getElementById('table1');//id=table1という要素を取得
        var row = table.insertRow(-1);//id=table1の中にtrタグを最後の子要素として追加
        var cells = new Array();
        for(var i = 0; i < table.rows[0].cells.length; i++){
            cells[i] = row.insertCell(-1);//新しく作ったrowの中にtrタグを最後の子要素として追加
            cells[i].innerText=document.getElementsByName("table1_cell_value")[i].value;
            document.getElementsByName("table1_cell_value")[i].value="";//入力フィールドの初期化
        }
    }
    //ここまで表の列追加のコード

    //ここからCSV出力&ダウンロード
    function handleDownload() {
        var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);//文字コードをBOM付きUTF-8に指定
        var table = document.getElementById('table1');//id=table1という要素を取得
        var data_csv="";//ここに文字データとして値を格納していく

        for(var i = 0;  i < table.rows.length; i++){
          for(var j = 0; j < table.rows[i].cells.length; j++){
            data_csv += table.rows[i].cells[j].innerText;//HTML中の表のセル値をdata_csvに格納
            if(j == table.rows[i].cells.length-1) data_csv += "\n";//行終わりに改行コードを追加
            else data_csv += ",";//セル値の区切り文字として,を追加
          }
        }

        var blob = new Blob([ bom, data_csv], { "type" : "text/csv" });//data_csvのデータをcsvとしてダウンロードする関数
        if (window.navigator.msSaveBlob) { //IEの場合の処理
            window.navigator.msSaveBlob(blob, "test.csv"); 
            //window.navigator.msSaveOrOpenBlob(blob, "test.csv");// msSaveOrOpenBlobの場合はファイルを保存せずに開ける
        } else {
            document.getElementById("download").href = window.URL.createObjectURL(blob);
        }

        delete data_csv;//data_csvオブジェクトはもういらないので消去してメモリを開放
    }
    //ここまでCSV出力&ダウンロード
</script>
  </body>
</html>

bodyタグ内のHTMLの解説(tableタグ編)

・HTMLで表示されている表は以下コードで作成されています。後述のjava scriptによって行を追加ボタンを押されたタイミングでtrタグ、thタグを新たに足していきます。

index.html
<table id="table1"  border="1" cellpadding="10">
 <tr>
   <th>店舗コード</th>
    <th>品目</th>
    <th>金額(円)</th>
  </tr>
</table>

bodyタグ内のHTMLの解説(入力フィールド編)

・2〜4行目では「店舗コード」、「品目」、「金額」用の入力フィールドを5,6行目では追加ボタン、CSVダウンロード用リンクを生成しています。
・name属性、id属性は後述のjava scriptで利用するために設定しています。
・add_line()関数、handleDownload()はそれぞれ表の行追加、csvファイルダウンロードを実行するための関数です。解説は後述の「java script scriptタグ内の解説(add_line()関数編)」、「java script scriptタグ内の解説(handleDownload()関数編)」で記載します。

index.html
<h5>↓入力フィールド</h5>
<input name="table1_cell_value" id="th_value1" type="text" placeholder="店舗コード">
<input name="table1_cell_value" id="th_value2" type="text" placeholder="品目">
<input name="table1_cell_value" id="th_value3" type="text" placeholder="金額(円)">
<input type="button" value="行を追加" onclick="add_line()">
<b><a id="download" href="#" download="test.csv" onclick="handleDownload()" align="right">csvファイルダウンロード</a></b>

scriptタグ内のjava scriptの解説(add_line()関数編)

・add_line()関数は入力フィールドに入力された文字・数字を表に列を追加する関数です。
・はじめのforループは入力フィールドが空白を防ぐためのコードです。空白の場合はアラートが出ます。
・9行〜17行目以降は、getElementById()関数でtableタブオブジェクトを取得しからinsertRow()関数、insertCell()を実行し行とセルを追加しています。引数に「-1」を指定することで最後尾に行とセルが追加されます。
・2つ目のforループでは新しくセルを追加したあとそのオブジェクトをcell配列に格納しています。格納後はinnerText属性に入力フィールドの入力値(value属性)を代入させています。

    //ここから表の行追加のコード
    function add_line() {
        for(var i = 0; i < document.getElementsByName("table1_cell_value").length; i++){
            if(document.getElementsByName("table1_cell_value")[i].value ==""){
                alert("未入力項目があります。");
                return false;
            }
        }

        var table = document.getElementById('table1');//id=table1という要素を取得
        var row = table.insertRow(-1);//id=table1の中にtrタグを最後の子要素として追加
        var cells = new Array();
        for(var i = 0; i < table.rows[0].cells.length; i++){
            cells[i] = row.insertCell(-1);//新しく作ったrowの中にtrタグを最後の子要素として追加
            cells[i].innerText=document.getElementsByName("table1_cell_value")[i].value;
            document.getElementsByName("table1_cell_value")[i].value="";//入力フィールドの初期化
        }
    }
    //ここまで表の列追加のコード

scriptタグ内のjava scriptの解説(handleDownload()関数編)

・詳細はソースコードのコメントアウトに書いてありますが、File APIの元となったBolb関数を用いてcsvファイルをBom付きUTF-8で出力させています、※エクセルで開ける用にするためBom付きにしています。
・forループ内はかなり原始的な処理をさせています。単純にdata_csvにtableタグ中のセルの値(thタグのinnerText属性値)を文字列として加算しています。if else文で「,」や改行コードを挟ませ、結果的にdata_csvに格納される文字列をcsv形式にしています。
※もっとうまい方法を思いつけば良かったのですが。。。。:weary:

    //ここからCSV出力&ダウンロード
    function handleDownload() {
        var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);//文字コードをBOM付きUTF-8に指定
        var table = document.getElementById('table1');//id=table1という要素を取得
        var data_csv="";//ここに文字データとして値を格納していく

        for(var i = 0;  i < table.rows.length; i++){
          for(var j = 0; j < table.rows[i].cells.length; j++){
            data_csv += table.rows[i].cells[j].innerText;//HTML中の表のセル値をdata_csvに格納
            if(j == table.rows[i].cells.length-1) data_csv += "\n";//行終わりに改行コードを追加
            else data_csv += ",";//セル値の区切り文字として,を追加
          }
        }

        var blob = new Blob([ bom, data_csv], { "type" : "text/csv" });//data_csvのデータをcsvとしてダウンロードする関数
        if (window.navigator.msSaveBlob) { //IEの場合の処理
            window.navigator.msSaveBlob(blob, "test.csv"); 
            //window.navigator.msSaveOrOpenBlob(blob, "test.csv");// msSaveOrOpenBlobの場合はファイルを保存せずに開ける
        } else {
            document.getElementById("download").href = window.URL.createObjectURL(blob);
        }

        delete data_csv;//data_csvオブジェクトはもういらないので消去してメモリを開放
    }
    //ここまでCSV出力&ダウンロード

参考サイト

・JavaScript のデータを CSV で保存する
・JavaScriptからtable要素を制御する方法
・javascript で作成したCSVファイルをエクセルで表示可能にする
・Blob と File クラスについて
・シンプルCSSテンプレート

あとがき

今回初めてQiitaに投稿してみましたが、書き方がわからず文字だらけに。。。:sweat_smile:
これからもちょこちょこ書き、ちょこちょこ見やすく分かりやすくしていきます!

shmiki
大学でメカトロを勉強後、某SIerにて勤務後、外資系ITベンダーへ転職。 皆さんのハマりポイントをソッコーで解決できることを目標に投稿します! 後は気ままに色々挑戦します! 縁が深くなったMicrosoft 系の内容が多くなるかもしれません。。。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした