スマホ全盛、呼吸するようにTwitterをするという時代、正直、ホームページへのアクセス数など日に数十PVあればいいものです。
そのために日々デザインに凝ることは正直時間と労力の無駄遣いでしょう。
今はむしろ、自身のITスキルの勉強の題材として活用していることも多いです。
社会人1年目の私は、実はある会社で乗換案内のプログラムを作っていたりします。
「KR鉄道館」を運営する中で、HTMLからCSS、JavaScriptくらいは一通り経験していますが、C言語でさえ就職して初めて触ったもの。
なにぶんにもIT技術に関する専門教育を受けてきたわけではないため、実務のためには自分でいろいろ勉強してみる必要があります。
今回はサイトの情報をデータベース管理するため、JSONを導入してみました。
そもそも今ほとんどホームページを更新できていないのですが、たまに更新するときにいつも行っているルーチンワークで、こんな悩みがありました。
- 更新情報の作成に、細かい作業が多く、煩雑である。
- 更新情報をトップページと更新情報ページのそれぞれに同一内容を表示させているのに、それぞれ手作業で更新していたため、面倒である。
そこで今回、次のような仕様で、更新作業を簡略化すべく考えました。
- 更新情報データはデータベース化して、複数のページから同じファイルに情報を呼びにいく。
- HTMLを自動生成する際、トップページ用と更新情報ページ用で、生成方法を分ける。
- トップページ用:データベースのうち最新の5件を表示する。
- 更新情報ページ用:指定された年の情報をすべて表示する。
- 見た目はJSON導入前後で変化しないよう配慮する。
「てつたのアルバム」も、各アルバム情報(タイトル、キャプション、作成日等)のデータベースを作り、そこから各ページを接続させて構築しているのですが、JSONをまともに知らなかった時分、JavaScriptの配列を使ってこれを実現していました。
しかし、JavaScriptの配列をそのままデータベースとして使うのはあまりスマートとはいえないため、もっときれいに作りたいと考えていました。
そこで今回は、JSONを使ってデータベースを作り、そのデータを基に自動でページ内容が更新できるようにしました。
1.データベースの作成
JSONでデータを作ります。今回はこんな感じにしてみました。
data.json
{
"data" : [
{
"ymd" : 20170218,
"mark" : "a",
"url" : "album/",
"text" : "「増毛の記憶~2016」ほかアルバム3点を公開しました。"
},
{
"ymd" : 20160522,
"mark" : "o",
"url" : "selfintro.html",
"text" : "「管理人紹介」を更新しました。"
},
{
"ymd" : 20160604,
"mark" : "o",
"url" : "link.html",
"text" : "リンク集を更新しました。"
},
{
"ymd" : 20160529,
"mark" : "o",
"url" : "selfintro.html",
"text" : "「管理人紹介」を更新しました。"
},
{
"ymd" : 20160405,
"mark" : "o",
"url" : "selfintro.html",
"text" : "「管理人紹介」を更新しました。"
},
{
"ymd" : 19001231,
"mark" : "m",
"url" : "",
"text" : "JSONテストデータ3"
},
{
"ymd" : 19001101,
"mark" : "s",
"url" : "",
"text" : "JSONテストデータ2-1"
},
{
"ymd" : 19001031,
"mark" : "si",
"url" : "",
"text" : "JSONテストデータ2"
},
{
"ymd" : 19000101,
"mark" : "b",
"url" : "index.html",
"text" : "JSONテストデータ1"
}
],
"markdef" : {
"a" : "アルバム",
"b" : "BVE",
"s" : "駅",
"m" : "動画・音",
"si": "サイト",
"o" : "その他"
}
}
{ }(大かっこ)を使うと連想配列:オブジェクトを作成でき、「"key" : "value"」の形で値が設定できます。
また、[ ](角かっこ)を使うことで、0番から始まる各種プログラム言語でおなじみの配列をつくることができます。
今回はこれを組み合わせて、"data"のキーに各更新情報の配列をつくり、その中にそれぞれオブジェクトを作りました。
"ymd":更新日。データの整理に最も重要。
"mark":行頭のジャンルマーク。
"url":リンク先URL。ルートディレクトリを基準に相対パスで記述しています。
"text":更新情報文の本文。
最初の5件は実際に使うデータ、残りの1900年を指定したデータはテスト用のダミーデータです。
なお、"markdef"キーには、各ジャンルマークの記号に対する日本語名を定義しています。ジャンルマーク画像の代替テキストとして使用します。
2.JavaScriptコードの作成
そして、このJSONを呼び出すために書いたソースがこちらです。
updates.js
// HTTP通信用、共通関数
function createXMLHttpRequest(cbFunc)
{
var XMLhttpObject = null;
try{
XMLhttpObject = new XMLHttpRequest();
}catch(e){
try{
XMLhttpObject = new ActiveXObject("Msxml2.XMLHTTP");
}catch(e){
try{
XMLhttpObject = new ActiveXObject("Microsoft.XMLHTTP");
}catch(e){
return null;
}
}
}
if (XMLhttpObject) XMLhttpObject.onreadystatechange = cbFunc; //ファイルの読み込み完了時の動作
return XMLhttpObject;
}
// document.getElementById
function $(tagId)
{
return document.getElementById(tagId);
}
function writeData(position, mode) //ファイル名、表示するID、動作モード
{
fName = 'updates/data.json'
httpObj = createXMLHttpRequest(function(){displayData(position, mode)}); //クロージャにすると引数を収められる
if (httpObj)
{
//リクエストのタイプを設定
httpObj.open("GET", fName, true);
//キャッシュは読みに行かせない
httpObj.setRequestHeader('Pragma', 'no-cache');
httpObj.setRequestHeader('Cache-Control', 'no-cache');
httpObj.setRequestHeader('If-Modified-Since', 'Sat, 01 Jan 2000 00:00:00 GMT');
//リクエストを送信
httpObj.send();
}
}
function displayData(position, mode)
{
if ((httpObj.readyState == 4) && (httpObj.status == 200)) //読み込み完了
{
$(position).innerHTML = parseJSON(httpObj.responseText, mode);
}
}
// JSONのデータを解析して表示
function parseJSON(jsData, mode)
{
var data = JSON.parse(jsData);
var resultData = "";
for(var i=0; i<data.data.length; i++)
{
//データを変数に収める
var ymd = data.data[i].ymd; // 日付
var mark = data.data[i].mark; // マーク記号
var url = data.data[i].url; // URL
var text = data.data[i].text; // 情報内容
var hit = false;
if(mode >= 1900){ //年指定モード
if(Math.floor(ymd / 10000) == mode){
hit = true;
}
}
else{ //表示数指定モード
if(i < mode){
hit = true;
}
else{
break;
}
}
if(hit){
var showymd = (("0" + Math.floor(ymd / 10000) % 100).slice(-2)) + '.' + Math.floor(ymd / 100) % 100 + '.' + ymd % 100;
resultData += '<p><img src="m_' + mark + '.gif" width="50" height="15" alt="' + data.markdef[mark] + '" class="umark">'
+ showymd + ' ';
//URL指定がないときはリンクを外す
if(url != ""){
resultData += '<a href="' + url + '">';
}
resultData += text;
if(url != ""){
resultData += '</a>';
}
resultData += '</p>';
}
}
return resultData;
}
ページには次のようにしてコードを埋めました。
(<head>~</head>内)
<script type="text/javascript" src="updates/updates.js"></script>
(トップページ:<body>~</body>内)
<div id = "jsondata"></div>
<!-- JSON化 2017.2.18 -->
<script type="text/javascript">
writeData('jsondata', 5)
</script>
(更新情報ページ:<body>~</body>内)
<div id="u_body2">
</div>
<script type="text/javascript">
writeData('u_body2', 2017)
</script>
各ページから、writeData関数で指定したIDの位置に情報を出力します。
引数として、第一引数に出力先のID、第二引数に出力モードを設定しました。
第二引数の出力モードは、1900以上の時はその年の全データを出力、1900未満の時は最新のデータから指定した件数のデータを出力させることとしました。
このとき、引数に、JSONファイル(data.json)の読み込みが終了したときに行う処理を記載するのですが、関数を記載する際、そのまま「関数(引数)」と記述すると、指定した関数の実行前に引数の内容が評価され実行されてしまいます。
そうではなくて、ちゃんと必要な時にその引数を伴って関数が実行できるようにするために、
クロージャの形を使って、処理させたい関数をもう一度"function(){}"の形で包んであげると、中身を実行させないで引数として渡すことができました(31行目)。
無事にXMLHttpRequestオブジェクトが作成できれば、このオブジェクトのhttpObj.onreadystatechangeの中に先の関数名を埋め込みます(18行目)。
ブラウザはJSONデータについて、結構キャッシュを利用してしまいます。
更新情報は常に最新のものを使用したいので、ブラウザのキャッシュは使用せず、都度ファイルを読みにいくように設定しています(37~39行目)。
さて、data.jsonが読み込まれると、関数displayData()が実行されます。
onreadystatechangeはreadystateが変化するたびに呼び出されますが、
readystate=4、status=200のときがロード成功を指すそうなので、このときに関数parseJSON()を呼びます。
この第一引数のhttpObj.responseTextに、data.jsonの文字列が入っています。
まず54行目で、eval()関数JSON.parse()がdata.jsonの内容を解釈してJavaScriptで使うオブジェクトの形に変換します。
「この関数一つでJSONを解釈できる、だからとても扱いやすい!」が標榜されているのですが、JSONの記述法は結構厳しめで、ここでちゃんとエラーなく変換できるまでに時間がかかりました。
現在eval()関数は推奨されていないということで、記法を訂正しました。ご指摘いただきました竹麻呂さま、ありがとうございました。(2/19 4:30)
更新情報データの一つ一つがオブジェクトとその配列で構成されているので、for文をぶん回して項目ごとに内容を解釈していきます(59~62行目)。
ここでやっと、HTMLから引き継いできた引数modeを使います。
modeが1900以上なら年指定モードとみなし、指定した年のデータのみを使うように処理します。
また、modeがそれ未満なら表示数指定モードとし、ループカウンタ変数iの値を利用して指定個数だけデータを抽出します。
抽出したデータだけを変数resultDataに蓄積して返り値としてリターンします。
$(position).innerHTMLを使って、positionに指定したIDの位置にデータを書き出します。
document.getElementByldという長ったらしいオブジェクト名を省略するために、$()という関数の形に括って出力しているそう。jQueryでよく使う記法らしいです。
こうして一日かけて更新情報データベースのJSON化が完成しました。
実際のところ、この工数にペイできるほど今後の更新があるのか未知数ですが(笑)、現在様々な分野で導入が進むJSONを学ぶいい機会になったと思います。
ITは目的ではなく手段でなければなりませんので、やりたいことを確実に実現させる方法として、必要なものはこれからも吸収していきたいと思っています。
参考文献