1. はじめに
これまでの計算結果をCVSファイルで出力するプログラムを加えてみます。これによってエクセルとの連携ができるようになり、活用範囲が広がります。ファイルへの出力とは言うもの、HTMLなので、指定されたULRからダウンロードするという立場をとります。<a>タグのdownload属性を使います。短いプログラムですが、初めて見る命令が多く出てきます。面食らってしまいそうですが、「CVS保存はこのようにするのだ」と丸呑みしておけば、とりあえず、良いと思いました。
2. 付加・修正する部分
- (1) HTML内にボタンを設置し、押した時に、関数「保存」が動くようにする
- (2) 関数「面積計算」の結果を、関数「保存」に渡す(戻り値)
- (3) 戻り値データ(底辺、高さ、面積)を使って、新たなデータを作る
- 生成データ⇒「“底辺”、数値、“高さ” 、数値、“面積” 、数値」
- (4) 上記をテキストデータに変換(区切りはコンマ(“,”)、windowsで改行は「\r\n」)
- (5) テキストデータに、ファイルの形式を示す「バイトオーダーマーク」を付加
- 生成データ⇒「(バイトオーダーマーク)“底辺”(コンマ)数値(改行)“高さ”(コンマ)数値(改行)“面積”(コンマ)数値(改行)」
- (6) テキストデータをバイナリ化して、変数に格納
- (7) バイナリデータを格納した変数の仮想的なURLを取得
- (8) HTMLに<a>を設置し、JavaScriptからdownload属性を付加し、上記のバイナリデータを出力(ダウンロード)
3. プログラム1;<a>をHTML内に置く場合
- (1) HTML内で「保存」を発動するボタンは、<input>を使って設置
- (2) 関数「面積計算」の最後に「return [底辺,高さ,面積];」を加え、関数の戻り値を指定。これにより、関数「面積計算」には、[底辺,高さ,面積]の3つの値が入っていることになる
- (3) 関数「面積計算」の戻り値を、変数「result」に格納し、resultの各値に名称「底辺」「高さ」「面積」付け、新たな配列変数record1を作る。生成するデータは、[[“面積”,数値],[“高さ”,数値],[“面積”,数値]]の2×3の配列(resultは勝手に配列変数にる)
- (4) 配列を「join」で文字列に変換する。「let data1 =record1.map((row) => row.join(',')).join('\r\n');」では、配列record1(2×3)の2要素をjoin”,”でコンマ区切り(CSV)の3要素の配列に変換し、その結果をさらに、join('\r\n')で改行区切りの文字列に変換し1行の文字列とし、その結果をdata1という変数に格納
- なお、mapは「map(関数)」の形式になるので、rowは関数で、その関数は「=>(アロー関数)」で定義しています。この場合、関数は「row.join(',')」で、mapを使ってrecord1の各要素に適用し、3要素の配列を得ている。
- (5) let bom = new Uint8Array([0xEF, 0xBB, 0xBF]);は、UTF-8エンコードのCSVファイルの先頭に付加する記号「バイトオーダーマーク(BOM)」です。
- ここは、あまり考えこまずに、エクセルのCSVデータの先頭には、このBOMデータを付加すると丸呑みする。newは新たな命令のテンプレート(ひな形)を呼び出す時に使う命令と覚えておく。
- (6) let blob = new Blob([ bom, data1 ], { type : 'text/csv' });、new Blobは、前の行で作ったbomとdata1を結合して、1つのバイナリーオブジェクトを作る命令です。これで、変数blobに保存(出力)するバイナリデータが格納できた
- (7) let outfile1 = document.getElementById("export1");、HTML内のid=export1部分の要素をoutfile1に取り込みます。この要素は<a>です。
- (8) outfile1.download = 'sample.csv';
- outfile1.href = URL.createObjectURL(blob);
- outfile1.click();、
- この一連の作業は、<a>outfile1のdownload属性において、ファイル名を“sample.cvs”にして、目的のデータの仮想URLを取得(どこにあるか分からないけど、こう書けばURLとして繋いでくれる)し、click()メソッドで<a>をクリックしたものとして、ダウンロードを開始する
リスト3-1
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>save</title>
- </head>
- <body>
- <p>HTMLからのinputによる入力、結果はHTMLに表示</p>
- 三角形の面積
- <p><label>input底辺:<input type="number" id="input1"></label></p>
- <p><label>input高さ:<input type="number" id="input2"></label></p>
- <p><input type="button" value="入力終了・計算開始" id="計算開始"></p>
- <p id="p1">表示1</p>
- <label>結果を保存(ダウンロード)しますか?<input type="button" value="保存" id="保存"></label>
- <a id="export1"></a>
- <script>
- document.getElementById('計算開始').addEventListener('click', 面積計算);
- function 面積計算(){
- let 底辺 = document.getElementById("input1").value;
- let 高さ = document.getElementById("input2").value;
- let 面積 = (底辺 * 高さ)/2;
- document.getElementById("p1").innerHTML = "底辺" + 底辺 + " 高さ" + 高さ + " 面積" + 面積;
- return [底辺,高さ,面積];
- };
- document.getElementById('保存').addEventListener('click', 保存);
- function 保存(){
- let result = 面積計算();
- let record1 = [['底辺',result[0]],['高さ', result[1]],['面積', result[2]]];
- let data1 = record1.map((row) => row.join(',')).join('\r\n');
- let bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
- let blob = new Blob([ bom, data1 ], { type : 'text/csv' });
- let outfile1 = document.getElementById("export1");
- outfile1.download = 'sample.csv';
- outfile1.href = URL.createObjectURL(blob);
- outfile1.click();
- return;
- };
- </script>
- </body>
- </html>
4. プログラム2;<a>をJavaScriptの中に入れる場合
上の例では、<a>で保存機能(ダウンロード)を実現していることを明確にするために、HTML内に<a>を置きましたが、<a>をJavaScriptの中で発生させて、保存を実現することもできます。JavaScripの中だけでプログラムが作れるようになります。
変更はわずかで、以下の2点です。
- (1) 「let outfile1 = document.getElementById("export1");」を「let outfile1 = document.createElement('a');」に変える。<a>を作る命令ですね。(参考)ここで作られた<a>のHTML内での位置は、現時点では決まっていません。位置を決めるためにはelemnt.insertBefore()を使います。
- (2) HTML内の<a>の行を消去します。
(最終プログラムのHTML画面とエクセルによるCSVファイルの表示)
リスト3-2
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>save</title>
- </head>
- <body>
- <p>HTMLからのinputによる入力、結果はHTMLに表示</p>
- 三角形の面積
- <p><label>input底辺:<input type="number" id="input1"></label></p>
- <p><label>input高さ:<input type="number" id="input2"></label></p>
- <p><input type="button" value="入力終了・計算開始" id="計算開始"></p>
- <p id="p1">表示1</p>
- <label>結果を保存(ダウンロード)しますか?<input type="button" value="保存" id="保存"></label>
- <script>
- document.getElementById('計算開始').addEventListener('click', 面積計算);
- function 面積計算(){
- let 底辺 = document.getElementById("input1").value;
- let 高さ = document.getElementById("input2").value;
- let 面積 = (底辺 * 高さ)/2;
- document.getElementById("p1").innerHTML = "底辺" + 底辺 + " 高さ" + 高さ + " 面積" + 面積;
- return [底辺,高さ,面積];
- };
- document.getElementById('保存').addEventListener('click', 保存);
- function 保存(){
- let result = 面積計算();
- let record1 = [['底辺',result[0]],['高さ', result[1]],['面積', result[2]]];
- let data1 = record1.map((row) => row.join(',')).join('\r\n');
- let bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
- let blob = new Blob([ bom, data1 ], { type : 'text/csv' });
- let outfile1 = document.createElement('a');
- outfile1.download = 'sample.csv';
- outfile1.href = URL.createObjectURL(blob);
- outfile1.click();
- return;
- };
- </script>
- </body>
- </html>