JavaScriptでpageのinclude
デモ画面を作ってるときに思った。
サーバーなしで、ローカルディスクからHTMLを表示するときにもTilesやPage Includeのような機能がほしいなーって。
そこでちょっとJavaScriptの勉強をしてみた。Ajaxとかはやってるし。っと思って。
勉強してて思ったけど、XMLHttpRequest#open()でローカルファイル指定してもやっぱり読み込めない。あたりまえといえばあたりまえだけど。
どうしようかなーって迷ったけど、適当にframeに分けて、読み込み対象となるようにすることにした。
ディレクトリ構成はこんな感じ
page\layout\ controlFrame.html <- frameに分けたりする。処理の中心 layout.html <- レイアウトを定義 header.html <- include対象のHTML。ヘッダ情報を定義 footer.html <- include対象のHTML。フッタ情報を定義 one.html <- メインのHTML。1つ目 two.html <- メインのHTML。2つ目 page\js\ includeDefaultConf.js <- ちょっとした設定情報 includeFrame.js <- frame内容を作るときに利用 includePage.js <- HTMLのincludeを行う includeJump.js <- controlFrameに飛ばす
まずはそれぞれのHTMLから
controlFrame.htmlはわかりづらい。各frameタグのname属性は大切。
<html> <head> <title id="include_title"></title> </head> <script type="text/javascript" src="../js/includeDefaultConf.js"></script> <script type="text/javascript" src="../js/includeFrame.js"></script> <script type="text/javascript"> <!-- var control = new PageController(); control.addIncludePage("header.html", "headerPage"); control.addIncludePage("footer.html", "footerPage"); control.addIncludePage("header.html", "headerPage"); control.setLayoutPageURL("layout.html"); control.setMainPageName("mainPage"); control.write(); // 上と同じことをしてる。どっちがわかりやすいか。 //document.open(); //document.writeln('<frameset rows="0,0,0,*" frameborder="0">'); //document.writeln(' <frame src="header.html" name="headerPage">'); //document.writeln(' <frame src="footer.html" name="footerPage">'); //document.writeln(' <frame src="' + getMainPageURL() + '" name="mainPage">'); //document.writeln(' <frame src="layout.html" name="' + LAYOUT_PAGE_NAME + '">'); //document.writeln('</frameset>'); //document.close(); // --> </script> </html>
layout.htmlは普通。divタグのid属性がポイント。
<html> <head> <meta http-equiv="content-type" content="text/html;charset=Shift-JIS"> <title>Layout Page</title> <script type="text/javascript" src="../js/includeDefaultConf.js"></script> <script type="text/javascript" src="../js/includePage.js"></script> </head> <body onload="javascript:doExecute();"> <table border="1"> <tr><td> <div id="include_headerPage#header"></div> </td></tr> <tr><td> <div id="include_mainPage#contents"></div> </td></tr> <tr><td> <div id="include_footerPage#footer"></div> </td></tr> </body> </html>
header.htmlとfooter.htmlはそのまま。
<html> <head> <meta http-equiv="content-type" content="text/html;charset=Shift-JIS"> </head> <body> <div id="header"> これは、デモ画面(紙芝居)作成時にでもInclude等の機能により画面の共通化を促進するためのJavaScriptです。<br> ローカルで動くことを期待しています。<br> </div> </body> </html>
<html> <head> <meta http-equiv="content-type" content="text/html;charset=Shift-JIS"> </head> <body> <div id="footer"> Copyright (C) 2005 とか表示する。 </div> </body> </html>
one.htmlとtwo.htmlは中心となる画面。それぞれこんな感じ
<html> <head> <meta http-equiv="content-type" content="text/html;charset=Shift-JIS"> <title>One Page</title> <script type="text/javascript" src="../js/includeDefaultConf.js"></script> <script type="text/javascript" src="../js/includeJump.js"></script> </head> <body> <div id="contents"> ここはオリジナルの部分。<br> <a href="two.html">2つ目へ</a> </div> </body> </html>
<html> <head> <meta http-equiv="content-type" content="text/html;charset=Shift-JIS"> <title>Two Page</title> <script type="text/javascript" src="../js/includeDefaultConf.js"></script> <script type="text/javascript" src="../js/includeJump.js"></script> </head> <body> <div id="contents"> 2つ目のページ。<br> <a href="one.html">1つ目へ</a> </div> </body> </html>
HTMLはこんな感じ。
次はJavaScript
includeDefaultConf.jsは
var CONTROL_PAGE_PATH = "controlFrame.html"; var CONTROL_SIGN = "MARK"; var MAIN_PAGE_NAME = "mainPageName"; var LAYOUT_PAGE_NAME = "layoutPageName";
で、includeFrame.jsは、本当はこんなに必要ないと思うけど作ってしまった。
function PageController() { this.includePages = new Array(); this.mainPage = new FrameTag(getMainPageURL(), MAIN_PAGE_NAME); this.layoutPage = null; this.addIncludePage = function(url, name) { this.includePages.push(new FrameTag(url, name)); } this.setMainPageName = function(name) { this.mainPage = new FrameTag(getMainPageURL(), name); } this.setLayoutPageURL = function(url) { this.layoutPage = new FrameTag(url, LAYOUT_PAGE_NAME); } this.write = function() { document.open(); document.writeln('<frameset rows="' + this.getRowsStr() + '" frameborder="0">'); for (var i = 0; i < this.includePages.length; i++) { this.includePages[i].write(); } this.mainPage.write(); if (this.layoutPage != null) { this.layoutPage.write(); } document.writeln('</frameset>'); document.close(); } this.getRowsStr = function() { var result = ""; for (var i = 0; i < this.includePages.length; i++) { result += ", 0"; } if (this.layoutPage != null) { result += ", 0"; } result += ", *"; return result.substring(1); } } function FrameTag(url, name) { this.url = url; this.name = name; this.write = function() { document.writeln(' <frame src="' + this.url + '" name="' + this.name + '">'); } } function getMainPageURL() { return window.location.hash.substring(1) + window.location.search + "#" + CONTROL_SIGN; }
includePage.jsは中心となる大切なjs
function doExecute() { // Include先のページをきちんと読み込むまで待つ方法がわからなかったので、 // 適当にSleepすることにした。 sleep(); var divElements = document.getElementsByTagName("DIV"); for (var i = 0; i < divElements.length; i++) { if (isIncludeTarget(divElements[i])) { var includeTag = new IncludeTag(divElements[i]); includeTag.display(); } } } function sleep() { for (var i = 0; i < 1000; i++) { for (var j = 0; j < 1000; j++) { } } } function isIncludeTarget(element) { var targetId = element.id; var result = targetId.match(/^include_/); return result != null; } function IncludeTag(element) { this.targetId = element.id; this.display = function() { document.getElementById(this.targetId).innerHTML = window.top[this.getName()].document.getElementById(this.getId()).innerHTML; } this.getName = function() { var str = this.targetId.match(/^include_/); var result = this.targetId.replace(str, ""); str = result.match(/#.*$/); return result.replace(str, ""); } this.getId = function() { var str = this.targetId.match(/^include_.*#/); return this.targetId.replace(str, ""); } }
includeJump.jsはframe分割するための画面に飛ばす役目
if (!isExistControlPage()) { window.location.href = CONTROL_PAGE_PATH + window.location.search + "#" + getContentsURL(); } else { if (!isPageFromControlPage()) { window.open(getContentsURL() + window.location.search, "_top"); } } function isExistControlPage() { return isExistMainPage() || isExistLayoutPage(); } function isExistMainPage() { return window.top[MAIN_PAGE_NAME] != undefined; } function isExistLayoutPage() { return window.top[LAYOUT_PAGE_NAME] != undefined; } function isPageFromControlPage() { var mark = window.location.hash; return mark == ("#" + CONTROL_SIGN); } function getContentsURL() { return window.location.protocol + "//" + window.location.host + window.location.pathname; }
これで、one.htmlかtwo.htmlをダブルクリックすればlayoutを適用した画面が表示される。
結局、Ajaxの勉強にはならなかった・・・。けど、目的はとりあえず達成!!
frame以外の方法でも実現できればいいけど、histry.back()に依存したデモ画面でなければ問題ないから、気にしないっと。