プログラマ・アゲイン blog

還暦を過ぎたけどプログラマ復帰を目指してブログ始めました

Jsonデータを扱うJavaScriptプログラムを作ってみた

前前回の「Jsonデータを扱うRubyJavaScriptのプログラムを作ってみた」ブログの続きです。

Jsonのデータを、Webブラウザーに表示するJavaScriptを作成しました。

ここでは、ローカルに作成済みのJsonファイルを読んで、ブラウザーのBody部に動的にデータを組み込むHTML、JavaScriptプログラムを記録したいと思います。

 

pagain.hatenablog.com

 

 

HTML

画面の構造

Webブラウザーに表示するのはHTMLで記述されたデータなので、先ず、画面の構造をパーツとして整理してみます。

 

「header」は、通常のヘッダー・タグです。
「id=currentdate」と「id=groupname」は、spanタグで指定した範囲にid属性を付けて、JavaScriptから情報を表示させる部分です。

「form name=myform」は、inputタグを持った入力フォームで、Jsonファイルをファイル・エクスプローラで選択する部分です。

「form id=search」は、formタグで入力エリアとボタンをもった入力フォームで、検索用地域名の入力や表をクリアするボタンなどの部分です。

「thead」は、通常の表のヘッダー・タグです。

「tbody」は、「id=datatable」という名前を付けた表の本体であり、Jsonのデータを表示させる部分です。

HTMLのソース

HTMLのソースの全体は、以下のようになりました。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Display Json Data</title>
    <link href="./css/style.css" rel="stylesheet">
  </head>

  <body>
    <header>
      <div class="container">
        <h1>支店別売上高結果</h1>
      </div> <!-- /.container -->
    </header>

    <p>現在日時:<span id="currentdate"></span> 地域名:<span id="groupname"></span></p>
    <form name="myform"><input name="myfile" type="file" /></form>
    <form action="#" id="search">
      地域名:
      <input type="text" name="group">
      <input type="submit" value="検索" onclick="search.key.value='search'">
      <input type="submit" value="クリア" onclick="search.key.value='clear'">
      <input name="key" type="hidden" value="">
      <span id="message"></span>
    </form>

    <main>
      <div class="container">
        <section>
        <!-- <p id="jsondata">JSONファイルを読み込んでparseした結果を表示します</p> -->
          <table border=1>
            <thead>
              <tr>
                <th class="td1">地域</th><th class="td2">店番</th><th class="td3">支店名</th>
                <th class="td4">製品A</th><th class="td5">製品B</th><th class="td6">製品C</th>
                <th class="td7">製品D</th>
              </tr>
            </thead>
            <tbody id="datatable"></tbody>
          </table>
        </section>
      </div><!-- /.container -->
    </main>
    
    <script src="./js/script.js"></script>
    
  </body>
</html>

42行目で、別ファイルにしているJavaScriptを読み込んでいます。

次に今回のメインのJavaScriptについて、メモします。

 

JavaScript

JavaScriptのソース

JavaScriptのソースの全体は、以下のようになりました。

'use strict';
const form = document.forms.myform;
let jsonobj;
form.myfile.addEventListener( 'change', function(e) {
  const now = new Date();
  const year = now.getFullYear();
  const month = now.getMonth();
  const date = now.getDate();
  const hour = now.getHours();
  const min = now.getMinutes();
  const sec = now.getSeconds();
  let output = `${year}/${month + 1}/${date} ${hour}:${min}:${sec}`;
  document.getElementById('currentdate').textContent = output;
  var result = e.target.files[0];
  //FileReaderのインスタンスを作成する
  var reader = new FileReader();
  //読み込んだファイルの中身を取得する
  reader.readAsText(result);
  //ファイルの中身を取得後に処理を行う
  reader.addEventListener( 'load', function() {
    //ファイルの中身をJSON形式に変換する
    jsonobj = JSON.parse(reader.result);
  })
})

document.getElementById('search').onsubmit = function(event) {
  event.preventDefault();
  document.getElementById('message').textContent = '';
  if (search.key.value == 'clear') {
    clearList();
  } else if (search.key.value == 'search') {
    const groupid = document.getElementById('search').group.value;
    const regex = /^[\w@&]+/;
    clearList();
    if (groupid.length === 0) {
      //JSONオブジェクトの全データをtextarea内に表示する
      document.getElementById('groupname').textContent = '全て';
      putList();
    } else {
      if (regex.test(groupid)) {
        document.getElementById('groupname').textContent = groupid;
        document.getElementById('search').group.value = '';
        //該当グループのオブジェクトをtextarea内に表示する
        putGroup(groupid);
      } else {
        document.getElementById('message').textContent = '英数字(空白を含む)または特殊記号(@、&)のクラス名を入力してください';
      }
    }
  }
};

//JSONオブジェクトの全データをtextarea内に表示するfunction
function putList() {
  Object.keys(jsonobj).forEach(function(group) {
    setTbody(group);
  })
}

//該当グループのオブジェクトをtextarea内に表示するFunction
function putGroup(groupid) {
  Object.keys(jsonobj).forEach(function(group) {
    if (groupid.length === 1) {
      if (group.startsWith(groupid.toUpperCase()) || group.startsWith(groupid.toLowerCase())) {
        setTbody(group);
      }
    } else {
      if (group.toUpperCase().indexOf(groupid.toUpperCase()) != -1
         || group.toLowerCase().indexOf(groupid.toLowerCase()) != -1) {
        setTbody(group);
      }
    }
  })
}

//テーブルにデータをセットするfunction
function setTbody(group) {
  let data = jsonobj[group];
  for(var record of data){
    datatable.innerHTML += `<tr><td class="td1">${group}</td><td class="td2">${record.店番}</td>
      <td class="td3">${record.支店名}</td><td class="td4">${record.製品A}</td>
      <td class="td5">${record.製品B}</td><td class="td6">${record.製品C}</td>
      <td class="td7">${record.製品D}</td></tr>`
  }
}

//リストを消去するfunction
function clearList() {
  document.getElementById('search').group.value = "";
  let tbodies = document.getElementsByTagName("tbody");
  for (var i = 0; i < tbodies.length; i++) {
    while (tbodies[i].rows.length > 0) {
      tbodies[i].deleteRow(0);
    }
  }
}

Jsonに関わるところについて、以下にメモします。

Jsonファイルの読み込み

考慮点

今回一番悩んだのは、ローカルにおいているJsonファイルの読み込みでした。

HTMLを読み込んだ時点で、自動的にJsonファイルを読み込むようにしたかったのですが、それがうまく出来ませんでした。

どうやらブラウザーのセキュリティ制約により、ローカルのファイルの読み込みはブロックされるようです。

何とか以下のページで紹介されている方法で回避させることもできそうですが、まだちょっと私には難しいと思って断念しました。

ローカル(file:///)上で外部ファイル読み込みのセキュリティ制約を回避するいくつかの方法 - Qiita

そこで、以下のページを参考に、File APIを使用した読み込みとしました。

これが一般的な方法のようです。

www.sejuku.net

ただ、ブラウザーを立ち上げる度に、Jsonファイルを選択しないといけません。が、逆にいろいろなJsonファイルを指定できるので、良しとしています。

読み込み処理

Jsonファイルが選択されて、データを読み込む処理を以下の部分で行っています。

2  const form = document.forms.myform;
3  let jsonobj;
4  form.myfile.addEventListener( 'change', function(e) {
5    const now = new Date();
6    const year = now.getFullYear();
7    const month = now.getMonth();
8    const date = now.getDate();
9    const hour = now.getHours();
10   const min = now.getMinutes();
11   const sec = now.getSeconds();
12   let output = `${year}/${month + 1}/${date} ${hour}:${min}:${sec}`;
13   document.getElementById('currentdate').textContent = output;
14   var result = e.target.files[0];
15   //FileReaderのインスタンスを作成する
16   var reader = new FileReader();
17   //読み込んだファイルの中身を取得する
18   reader.readAsText(result);
19   //ファイルの中身を取得後に処理を行う
20   reader.addEventListener( 'load', function() {
21     //ファイルの中身をJSON形式に変換する
22     jsonobj = JSON.parse(reader.result);
23   })
24 })

 

2行目で、documentプロパティからmyformという名前のform要素を取得しています。

4行目で、inputタグのイベント処理を作成し、ファイルが選択されたことを検知します。

5行目から13行目までは、日付と時間を表示する処理です。

14行目は、選択されたファイルのファイル名を取得しています。

16行目で、ファイルを読み込むためのFileReaderオブジェクトのインスタンスを作成し、

18行目、でテキストとして選択されたファイルのデータを読み込んでいます。

20行目から23行目までで、読み込みが完了したら、そのテキスト・データをJson形式に変換しています。

変換には、JSONオブジェクトのparseメソッドを使用しています。

コンソールに「jsonobj」を出力してみると、以下のようなオブジェクトに変換されていることが判ります。

 

Jsonデータの表示

Json形式に変換されたjsonobj変数を使って、表示の処理を行います。

全件表示

25行目から49行目まででは、検索ボタンなどの処理を行っています。

地域名(検索キー)の入力欄になにも入れずに検索ボタンが押された場合には、全件のデータ全件を表示するようにしています。

データ全件を表示するために、次のputListファンクションを呼びだしています。

51 function putList() {
52   Object.keys(jsonobj).forEach(function(group) {
53     setTbody(group);
54   })
55 }

 

52行目で、Object.keys() メソッドを使用して、jsonobj変数が持つプロパティの 名前の配列(地域名)を、通常のループで取得するのと同じ順序で取得しています。

地域名が配列の形なので、forEachメソッドを使用して、与えられたファンクションを配列の各要素に対して一度ずつ実行します。

53行目のファンクションの中では、地域名を引数にして、setTbodyファンクションを呼びだしています。

setTbodyファンクションは共通に使用しているので、後述します。

検索キーに一致するデータのみ表示

検索用の文字が入力されて検索ボタンが押された場合には、該当のデータのみを表示するようにしています。

該当のデータのみを表示するために、次のputGroupファンクションを呼びだしています。

57 function putGroup(groupid) {
58   Object.keys(jsonobj).forEach(function(group) {
59     if (groupid.length === 1) {
60       if (group.startsWith(groupid.toUpperCase()) || group.startsWith(groupid.toLowerCase())) {
61         setTbody(group);
62       }
63     } else {
64       if (group.toUpperCase().indexOf(groupid.toUpperCase()) != -1
65          || group.toLowerCase().indexOf(groupid.toLowerCase()) != -1) {
66         setTbody(group);
67       }
68     }
69   })
70 }

 

58行目は、全件データ表示と同じ処理です。

59行目から68行目まででは、入力された検索キーと一致するデータを選別しています。

Jsonとは直接は関係ないですが、1文字のみ入力された場合には先頭文字を検索し、そうでない場合には入力された検索キーの文字が含まれているかどうかを検索しています。

いずれにしても、一致するものがあればsetTbodyファンクションを呼びだして表示させるようにしています。

Jsonデータの表示

Jsonデータは、HTMLの中に埋め込む形で画面に表示されるようにしています。

Jsonのデータを表示するために、次のsetTbodyファンクションを呼びだしています。

72 function setTbody(group) {
73   let data = jsonobj[group];
74   for(var record of data){
75     datatable.innerHTML += `<tr><td class="td1">${group}</td><td class="td2">${record.店番}</td>
76       <td class="td3">${record.支店名}</td><td class="td4">${record.製品A}</td>
77       <td class="td5">${record.製品B}</td><td class="td6">${record.製品C}</td>
78       <td class="td7">${record.製品D}</td></tr>`
79   }
80 }

 

73行目で、先ず検索キーに一致した地域名(グループ)の要素を取得します。

要素の取得は、rubyのハッシュと同じコーディングで取得します。

このJsonデータでは、要素は配列になっています。

74行目から79行目までで、配列から取り出したデータを、1件づつテーブル要素に追加しています。

 

Jsonの勉強の為にJavaScriptのプログラムを作成してみましたが、Rubyのハッシュと表記が似ており、JSONライブラリーもあるので、扱うのが簡単と感じました。

ただ、HTMLやJavaScriptのスキルが低いのでもっとスキルを付けていくのと、AjexとJsonを組み合わせたプログラムも機会があれば作ってみたいと思います。