HTML静的ページで部分的な別ファイルのソースを読み込む方法

2018年3月20日

HTML Javascript テクノロジー プログラミング

ホームページを制作する時に、色々な構成で構築する事ができますが、wordpressは、index.phpを基準にして、PHPで全て管理する方式ですが、データベース構成や、あまりにも無駄なPHPプログラムフローになっているため、構築直後は問題ありませんが、アクセスが据えてきた時に、いきなりWEBページが遅くなる事はよく聞く話です。 やはり静的ページのスピードが一番効率がいいのではないかと思い、自社のWEBページを作ってみたのですが、それでもやはりページ内の構成を効率的に行いたいのが正直なところです。

やりたい事

静的ページの構成ではHTMLファイル毎にデータを管理すればいいのですが、ヘッダやフッタなど、どのページも同じ事を書いている情報の部分が、ページの追加をした時や、書き直しが発生した時に全てのファイルを書き直すというのが非常に非効率に感じるはずです。 この図のようにHTMLに同じ要素を個別に保持している状態が一般的ですが、 参照できるようになると、修正が楽になります。 形としてはタグマネージャーに近い状態ですね。

やればいいじゃん

タグマネージャーを使ってこれらを行う事は可能ですが、他のシステムを組み込むというのが非常に無駄に感じるので、ここでの制約はHTML静的ページでローカル環境で行える事を前提にしたいと思います。 そして、これができる方法は以下の通り。
1. SSIを使ってincludeする。(ただしサーバー側で対応している必要があります。) 2. jQueryのload関数を使う。(そもそもjqueryを読み込みたい) 3. OSSのJSライブラリを使う。(不具合が発症している例も数多くあるらしい) 4. 独自のJSライブラリを使って簡易に読み込む
かつてHTML3ぐらいまでの王道はSSIでしたが、nginxなど最近の趣向としてSSIはFRAMESETと同じレベルで使わない方向のようです。 なんでかって?・・・たぶん、サーバーのデフォルト設定でSSIが有効になっていないから、環境構築時の制約が増えるのと、いざ本番にセットし忘れたら事故になる確率が高いからでしょうね。 jQyeryは、安定してますが、実は今回のこのやり方は、jQueryを含めたBootstrapモジュールを読み込む事を前提に考えたやり方なので、そもそもjqueryを張っているのであれば、loadを使うのが一番安定的でいいしょう。 しかし、jqueryモジュールほどの容量は重すぎると判断するエンジニアもいるので、4番の自己ライブラリがおすすめという事になります。 3番は書いてある通り、不具合のあるものが多いらしいですね。あまり関わらない方がいいかもしれません。

ソースコード

自己ライブラリのソースコードは以下になります。 $$MYNT_AJAX = (function(){ /** * Ajax * $$MYNT_AJAX | $$ajax({ * url:"", // "http://***" * method:"POST", // POST or GET * async:true, // true or false * data:{}, // Object * query:{}, // Object * querys:[] // Array * }); */ var $$ajax = function(options){ if(!options){return} var ajax = new $$ajax; var httpoj = $$ajax.prototype.createHttpRequest(); if(!httpoj){return;} // open メソッド; var option = ajax.setOption(options); // 実行 httpoj.open( option.method , option.url , option.async ); // type httpoj.setRequestHeader('Content-Type', option.type); // onload-check httpoj.onreadystatechange = function(){ //readyState値は4で受信完了; if (this.readyState==4){ //コールバック option.onSuccess(this.responseText); } }; //query整形 var data = ajax.setQuery(option); //send メソッド if(data.length){ httpoj.send(data.join("&")); } else{ httpoj.send(); } }; $$ajax.prototype.dataOption = { url:"", query:{}, // same-key Nothing querys:[], // same-key OK data:{}, // ETC-data event受渡用 async:"true", // [trye:非同期 false:同期] method:"POST", // [POST / GET] type:"application/x-www-form-urlencoded", // [text/javascript]... onSuccess:function(res){}, onError:function(res){} }; $$ajax.prototype.option = {}; $$ajax.prototype.createHttpRequest = function(){ //Win ie用 if(window.ActiveXObject){ //MSXML2以降用; try{return new ActiveXObject("Msxml2.XMLHTTP")} catch(e){ //旧MSXML用; try{return new ActiveXObject("Microsoft.XMLHTTP")} catch(e2){return null} } } //Win ie以外のXMLHttpRequestオブジェクト実装ブラウザ用; else if(window.XMLHttpRequest){return new XMLHttpRequest()} else{return null} }; $$ajax.prototype.setOption = function(options){ var option = {}; for(var i in this.dataOption){ if(typeof options[i] != "undefined"){ option[i] = options[i]; } else{ option[i] = this.dataOption[i]; } } return option; }; $$ajax.prototype.setQuery = function(option){ var data = []; if(typeof option.query != "undefined"){ for(var i in option.query){ data.push(i+"="+encodeURIComponent(option.query[i])); } } if(typeof option.querys != "undefined"){ for(var i=0;i<option.querys.length;i++){ if(typeof option.querys[i] == "Array"){ data.push(option.querys[i][0]+"="+encodeURIComponent(option.querys[i][1])); } else{ var sp = option.querys[i].split("="); data.push(sp[0]+"="+encodeURIComponent(sp[1])); } } } return data; }; $$ajax.prototype.loadHTML = function(filePath , selector){ $$ajax({ url:filePath, method:"GET", data:{ selector:selector }, async:true, onSuccess:function(res){ var target = document.querySelector(this.data.selector); if(!target){return;} // resをelementに変換 var div1 = document.createElement("div"); var div2 = document.createElement("div"); div1.innerHTML = res; // script抜き出し var scripts = div1.getElementsByTagName("script"); while(scripts.length){ div2.appendChild(scripts[0]); } // script以外 var num = 0; for(var i=0; i<div1.childNodes.length; i++){ if(div1.childNodes[num].nodeType !== 1){ num++; continue; } target.appendChild(div1.childNodes[num]); } // script $$ajax.prototype.orderScript(div2 , target); } }); }; $$ajax.prototype.orderScript = function(tags , target){ if(!tags.childNodes.length){return;} var div = document.createElement("div"); var newScript = document.createElement("script"); if(tags.childNodes[0].innerHTML){newScript.innerHTML = tags.childNodes[0].innerHTML;} if(tags.childNodes[0].src){newScript.src = tags.childNodes[0].src;} if(tags.childNodes[0].id){newScript.id = tags.childNodes[0].id;} if(tags.childNodes[0].className){newScript.className = tags.childNodes[0].className;} // if(typeof tags.childNodes[0].src === "undefined"){ target.appendChild(newScript); div.appendChild(tags.childNodes[0]); $$ajax.prototype.orderScript(tags , target); } else{ newScript.onload = function(){ $$ajax.prototype.orderScript(tags , target); }; target.appendChild(newScript); div.appendChild(tags.childNodes[0]); } }; return $$ajax; })(); これは過去に記事に書いた自己ajax.jsを改良したものです。 [Javascript] Ajaxライブラリ 追加ポイントは「$$ajax.prototype.loadHTML」関数と「$$ajax.prototype.orderScript」の2つです。

実行と考え方と躓きポイント

javascriptで他のファイルをajaxで読み込んでそのファイルの中のテキストを概要のエレメントにappendするだけでいいと最初は簡単に考えていたのだが、いくつか躓きポイントがありました。

躓きポイント

1. scriptタグは、読み込んだテキストをappendするだけでは、実行(src読み込み)されない。 2. 読み込みタイミングが常に非同期になってしまうため、bootstrapのjqueryが読み込まれていないエラーが発生
1番の解消は、読み込んだscriptタグをcreateElementする事で、実行可能なエレメントに変換する事ができます。 2番解消法は、javascriptに関して読み込み順番を厳密に行う。 これらを踏襲した形のソースコードになっています。 HTMLファイルを以下のように記述します。 <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="description" content=""> <meta name="author" content=""> <title>MYNT, Inc.</title> <script src="js/ajax.js"></script> <link href="css/index.css" rel="stylesheet"> <script> $$MYNT_AJAX.prototype.loadHTML("./html/head.html" , "head"); </script> </head> それに対して、head.htmlは以下のようになっています。 <link rel="icon" type="image/png" href="lib/img/favicon/favicon-32.png" sizes="32x32"> <link rel="icon" type="image/png" href="lib/img/favicon/favicon-64.png" sizes="64x64"> <link rel="stylesheet" href="vendor/bootstrap-3.3.7-dist/css/bootstrap.min.css"> <script src="vendor/jquery/jquery-3.2.1.min.js"></script> <script src="vendor/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script> <!--custom--> <link href="css/common.css" rel="stylesheet"> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-105017486-1', 'auto'); ga('send', 'pageview'); </script>

使い方

ページ内のHEADタグのできるだけ上位でajax.jsを読み込みます。 <script src="ajax.js"></script> 次に、読み込みたいファイルと、適用する場所(CSSのセレクタ記述)を引数にします。※下記は、headタグ内にソースコードを挿入する例 <script> $$MYNT_AJAX.prototype.loadHTML("./html/head.html" , "head"); </script> 冒頭に張ってあるリンクのページはこれでheadタグ内のソースが読み込まれ、メニュー部分とフッタ部分も別ファイル読み込みでサイト内の複数の静的ページで動作しています。 あら効率的!!!

最後に

ajaxコードは以前のオレオレコードですが、基本的にどのブラウザでも問題なく動いています。 安心してお使いください。 一番苦労した点は、JSの読み込み順番のところで、scriptタグのみ読み込みが完了したら、次のscriptタグを取り込む流れで行なっています。 さほど難しい書き方はしていないので、ソース解析してもらえれば、リレー方式は理解してもらえると思います。 あと気になるのは、CSSモジュールが読み込まれる前にHTMLソースが読み込まれると、style適合前の画面がチラッと見えてしまうところですね。 今後こういうのにも対応できる機能を持たせてみましょう。 実は、これができると、サーバー側はCGIを一切使わずに、静的ページを効率的に管理する事ができるようになるでしょう。 信じるか信じないかはあなた次第です。

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。