jQuery でファイルをインクルードする ( jQuery によるモジュール化)

  • 2008年11月10日 17:37
  • jQuery

jQuery でシンプルにファイルをインクルードする

通常のスタティックな Web サイトにおいて、Web ページの中で更新頻度の高いパーツを別ファイルにして、それをインクルードするシーンは多々あるかと思います。

Movable Type でも、モジュールやウィジェットを「インデックステンプレートのコンテキストで全ページに表示させたい」なんてとき、インデックステンプレートに読み込み用のファイルを作って、それを MTInclude で読み込むという手法で実現するのが一般的だと思います。

さて、ファイルを読み込む方法には、大きく分けると、サーバーサイドインクルード( SSI )とクライアントサイドインクルード( CSI )、がありますが、今回は後者の「クライアントサイド」で読み込む方法を jQuery を使ってやってみました。

いわゆる「 jQuery によるモジュール化」と言ったところです。

jQuery でのファイルの読み込みは、「Movable Type 備忘録 - jQuery でカンタンに外部ファイルを読込む(jquery.inc)」で紹介されている便利な jQuery プラグインを使う手もあります。手軽で便利なので試してみてください。

ただ、僕が使ったときは、ちゃんと UTF-8 で保存したのになぜか IE 6 で表示すると日本語が文字化けしてしまいました。なんで? また、インクルードしたファイルは、インクルードを指定した要素の子要素としてしか読み込めないようなので、ウィジェット部分の dt、dd 要素だけを読み込みたいときにはちょっと困りました。

そこで、今回は自分的にもう少し使いやすくした jQuery_inc.js を作ってみました。

メリット

この JavaScript のメリットをいくつかあげてみます。

複数のモジュール(パーツ)を 1 ファイルで管理

1 つのファイルから、読み込むモジュールをCSS セレクタのように指定するので、複数のパーツを 1 ファイルにまとめて管理できます。

1 モジュール = 1 ファイルだと、数が増えてくると管理が大変になります。その点、全てのモジュールを 1 ファイルにまとめられれば管理が楽です。

文字化けしない(と思う)

XHTML ファイルにまとめることで、head 要素内で文字コードも宣言するので、IE でも文字化けしないと思います。

モジュール用のファイルを作らなくても可能

極端な話、モジュール用のインデックステンプレートすら作らなくても良くなります。メインインデックス( index.html )からインクルードするという荒業も可能です。お勧めしませんが。。

読み込み元の要素を、読み込んだ要素で置換可能

「<div class="読み込み元"><p>ここに読み込む</p></div>」

のように子要素に読み込むだけでなく、

<div class="読み込み元"></div><p>ここに読み込む</p>

のように読み込み要素と置換することも可能です。

この柔軟性のおかげで、valid なソースを書きやすくなります。

読み込む要素を CSS セレクタなどで指定

モジュール用ファイルを編集しなくても、読込先を指定するセレクタを変更すれば、読み込む要素を変えられます。

完成したソース

/*
 * jQuery_inc.js
 *
 * Copyright (c) 2008 Tomohiro Okuwaki (http://www.tinybeans.net/blog/)
 * Licensed under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Modified: 2008-11-10
 * Document: http://www.tinybeans.net/blog/2008/11/10-173717.html
 *
 */

function strRef (text) {
	text = text.replace(/&amp;/g,'&');
	text = text.replace(/&lt;/g,'<');
	text = text.replace(/&gt;/g,'>');
	text = text.replace(/&quot;/g,'"');
	return text;
}
jQuery(document).ready(function(){
	jQuery('.jquery_inc').each(function(){
		var inc_url = '/jquery_inc.html';   /* <== Edit it first */
		var inc_selector = jQuery(this).attr('title');
		var replace_inc = jQuery(this).filter('.replace_inc').size();
		var file_inc = jQuery(this).filter('.file_inc').size();
		var child_inc = jQuery(this).filter('.child_inc').size();
		inc_selector = strRef(inc_selector);

		if (jQuery.browser.msie) {
			/* for IE [start] */
			if (file_inc) {
				inc_selector = inc_selector.replace(/ ?/,':');
				inc_selector = inc_selector.split(':');
				inc_url = inc_selector[0];
				inc_selector = inc_selector[1];
			}
			if (child_inc) {
				var matchStr = inc_url.match(' ');
				if (matchStr) {
					inc_selector = inc_selector + '>*';
				}
			}
			jQuery.ajax({cache: false});
			if (replace_inc) {
				jQuery(this).load(
					inc_url,
					function () {
						var default_content = jQuery(this).clone();
						var inc_content = default_content.find(inc_selector);
						jQuery(this).replaceWith(inc_content);
					}
				);
			} else {
				jQuery(this).load(
					inc_url,
					function () {
						var default_content = jQuery(this).clone();
						var inc_content = default_content.find(inc_selector);
						jQuery(this).html(inc_content);
					}
				);
			}
			/* for IE [ end ] */
		} else {
			if (file_inc) {
				inc_url = inc_selector;
			} else {
				inc_url = inc_url + ' ' + inc_selector;
			}
			if (child_inc) {
				var matchStr = inc_url.match(' ');
				if (matchStr) {
					inc_url = inc_url + '>*';
				}
			}
			jQuery.ajax({cache: false});
			if (replace_inc) {
				jQuery(this).load(
					inc_url,
					function () {
						var default_content = jQuery(this).clone();
						var inc_content = default_content.html();
						jQuery(this).replaceWith(inc_content);
					}
				);
			} else {
				jQuery(this).load(inc_url);
			}
		}
	});
});

このソースをコピーして保存するか、以下よりダウンロードしたファイルを解凍して保存します。

ダウンロード

jquery_inc.js の使い方

ここでは、Movable Type で使う場合を例に説明しますが、もちろん普通の Web サイトでも同様の考え方で使えます。

モジュール用ファイル(インデックステンプレート)の作成

まず初めに、モジュール用ファイルを作成しておきましょう。Movable Type の場合、モジュールとして読み込みたい部分をまとめてインデックステンプレートとして作成すればOKです。

ここでは、「Recent Comments (最近のコメント)」と「Recommended (オススメ)」をモジュール化してみます。

なお、読み込むファイルをできるだけ軽くするために、余計な要素は排除してシンプルにしましょう。また、以下の例では、検索ロボットは一応拒否しています。

テンプレートのデータ
  • インデックステンプレート
  • 出力ファイル名 : jquery_inc.html
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>jQuery Include - <$mt:BlogName$></title>
<meta name="ROBOTS" content="NONE" />
</head>
<body>

<dl id="wdg_include">
<dt>Recent Comments</dt>
<dd class="recentComments"><ul class="recentComments"><mt:Entries recently_commented_on="5">
<li><a href="<$mt:EntryLink$>"><$mt:EntryTitle$></a>
	<ul><mt:Comments>
	<li><a href="<mt:CommentEntry><$mt:EntryLink$></mt:CommentEntry>#comment<$mt:CommentID$>" title="<mt:SetVarBlock name="commentBodyCount"><$mt:CommentBody count_characters="1"$></mt:SetVarBlock><mt:If name="commentBodyCount" gt="50"><$mt:CommentBody remove_html="1" encode_html="1" trim_to="50" cat="..." regex_replace="/\n|\r\n/g",""$><mt:Else><$mt:CommentBody remove_html="1" encode_html="1"$></mt:If>" rel="nofollow"><mt:IfNonZero tag="CommentAuthor"><$mt:CommentAuthor$><mt:Else>No Name</mt:IfNonZero> - <$mt:CommentDate format="%m/%d"$></a></li></mt:Comments>
	</ul></li></mt:Entries>
</ul></dd>
<dt>Recommended</dt>
<dd class="recommended">
<p><a href="http://getfirefox.jp/"><img src="<$mt:BlogURL$>img/banner/firefox_125x125_rounded_blue.png" alt="Mozilla Firefox ブラウザ無料ダウンロード" title="Mozilla Firefox ブラウザ無料ダウンロード" width="125" height="125" /></a></p>
</dd>
</dl>
</body>
</html>

再構築したファイルは次のようになります。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja" dir="ltr">
<head>
<meta name="ROBOTS" content="NONE" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>jQuery Include - かたつむりくんのWWW</title>
</head>
<body>

<dl id="wdg_include">
    <dt>Recent Comments</dt>
    <dd class="recentComments">
        <ul class="recentComments">
            省略
        </ul>
    </dd>
    <dt>Recommended</dt>
    <dd class="recommended">
        <p><a href="http://getfirefox.jp/"><img src="http://www.tinybeans.net/blog/img/banner/firefox_125x125_rounded_blue.png" alt="Mozilla Firefox ブラウザ無料ダウンロード" title="Mozilla Firefox ブラウザ無料ダウンロード" width="125" height="125" /></a></p>
    </dd>
</dl>

</body>
</html>

以下の説明で出てくるサンプルも、基本的にこのモジュール用ファイルからインクルードすることにします。

jquery_inc.js の初期設定

jquery_inc.js の 22 行目当たりのファイルのパスを、作成したモジュール用ファイルのパスに変更します。

var inc_url = '/jquery_inc.html';   /* <== Edit it first */

jQuery などの読み込み

jquery_inc.js をサーバーにアップロードし、jQuery とともに読み込みます。ファイルへのパス部分は適宜変更してください。ここでは jQuery は 「AJAX Libraries API - Google Code」を使って読み込んでいます。

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("jquery", "1.2");</script>
<script type="text/javascript" src="/js/jquery_inc.js" charset="UTF-8"></script>

読み込みたい場所に title 属性、class 属性を設定

インクルードしたい場所のソースに、以下のような title 属性、class 属性を設定します。

title 属性(必須)

モジュール用ファイルの読み込みたい要素を jQuery のセレクタで指定します。jQuery のセレクタについては、次のページが参考になります。シンプルな CSS セレクタなら、ほぼそのまま使えます。

なお、< > & などは実体参照にすることを忘れずに。

jquery_inc クラス(必須)

jquery_inc.js を使ってモジュールを読み込みたい要素に設定します。必須です。

<Element class="jquery_inc" title="selector"> ~ </Element>

【サンプル】 dd.recommended 内の p 要素をインクルート

[読み込みたいソースは次の p 要素]

<dd class="recommended">
    <p><a href="http://getfirefox.jp/"><img src="http://www.tinybeans.net/blog/img/banner/firefox_125x125_rounded_blue.png" alt="Mozilla Firefox ブラウザ無料ダウンロード" title="Mozilla Firefox ブラウザ無料ダウンロード" width="125" height="125" /></a></p>
</dd>

[読み込みたい場所のソース]

<div class="jquery_inc" title="#wdg_List_RecentComments dd.recommended p">
    ここに読み込みます
</div>

[読み込み後のソース]

<div class="jquery_inc lastChild" title="#wdg_List_RecentComments dd.recommended p">
    <p><a href="http://getfirefox.jp/"><img src="http://www.tinybeans.net/blog/img/banner/firefox_125x125_rounded_blue.png" alt="Mozilla Firefox ブラウザ無料ダウンロード" title="Mozilla Firefox ブラウザ無料ダウンロード" width="125" height="125"></a></p>
</div>

child_inc クラス(オプション)

title 属性で指定した要素の「子要素」をインクルードしたい場合に設定します。

<Element class="jquery_inc child_inc" title="selector"> ~ </Element>

一つ手前のサンプルでは、セレクタで dd.recommended の子要素の p 要素まで指定しましたが、child_inc クラスをつければ少しシンプルになります。

[セレクタで指定]

<div class="jquery_inc" title="#wdg_include dd.recommended p">

[child_inc クラスで指定]

<div class="jquery_inc child_inc" title="#wdg_include dd.recommended">

このように子要素が一つのときは無意味に思えるかもしれませんが、dl 要素の子要素の dt、dd 要素など、複数の子要素をまとめてインクルードしたいときに威力を発揮します。

replace_inc クラス(オプション)

インクルードしたい場所の要素を、インクルードした要素と置き換えます。

<Element class="jquery_inc replace_inc" title="selector"> ~ </Element>

【サンプル】 span.test を dd.recommended 内の p 要素と置換

[読み込みたいソースは次の p 要素]

<dd class="recommended">
    <p><a href="http://getfirefox.jp/"><img src="http://www.tinybeans.net/blog/img/banner/firefox_125x125_rounded_blue.png" alt="Mozilla Firefox ブラウザ無料ダウンロード" title="Mozilla Firefox ブラウザ無料ダウンロード" width="125" height="125" /></a></p>
</dd>

[読み込みたい場所のソース]

<dl>
    <dt>Recommended</dt>
    <dd class="recommended">
        <span class="test jquery_inc replace_inc" title="dd.recommended p">ここに読み込みます</span>
    </dd>
</dl>

[読み込み後のソース]

<dl>
    <dt>Recommended</dt>
    <dd class="recommended lastChild">
        <p><a href="http://getfirefox.jp/"><img src="http://www.tinybeans.net/blog/img/banner/firefox_125x125_rounded_blue.png" alt="Mozilla Firefox ブラウザ無料ダウンロード" title="Mozilla Firefox ブラウザ無料ダウンロード" width="125" height="125"></a></p>
    </dd>
</dl>

file_inc クラス(オプション)

通常は、最初に設定した一つのファイルからモジュールをインクルードしますが、file_inc クラスをつけると、別のファイルからインクルードすることができます。その場合、title 属性を「(読み込みたいファイルの URL ) + (半角スペース) + (セレクタ)」と記述します。

<Element class="jquery_inc file_inc" title="URL selector"> ~ </Element>

【サンプル】 トップページの最新記事タイトルをインクルード

[読み込みたい場所のソース]

<p>最新の記事 : <span class="jquery_inc replace_inc file_inc" title="http://www.tinybeans.net/blog/ #primary h2 a:first">最新記事を読み込みます</span></p>

[読み込み後のソース]

<p>最新の記事 : <a href="http://www.tinybeans.net/blog/2008/11/05-124152.html">ブログ記事を「公開した日時」を自動的にカスタムフィールドで保存するカスタマイズ</a></p>

SEO 対策や JavaScript オフ時の対応

この jQuery を使ったインクルードは、読み込み後のコンテンツがソースに反映されていないため、SEO 的に若干問題があるかもしれません。また、JavaScript がオフの状態だと、全く機能しなくなります。

しかし、replace_inc クラスを使えば、読み込みたい場所のソースを丸々置換できるので、代替コンテンツを入れておくことが可能です。

例えば、Movable Type の場合、全ページに同じ「インデックステンプレートのコンテキストの最新のブログ記事」を表示したいけど、保険として「ブログ記事コンテキスト(カテゴリごと)の最新のブログ記事」を表示させておく、などの方法が可能です。

参考文献など

jQuery について本で勉強したい場合は、以下の書籍がお勧めです。

以上です。

トラックバック

The trackback URL is "".

コメント

TinyBeansさま

初めまして。
いつも、見やすくて、説明も丁寧なブログだなあと感心しています。

HTMLモジュールをインクルードするのに、jqueryメソッドの.html()を利用しようとしたら、(UTF-8を指定しても)IE6で文字化けするので悩んでいました。
TinyBeansさまのjsを利用してみたら、問題解決しました。
ありがとうございます。

ところで、jquery_inc.htmlに記述するモジュールのパスを、相対パス対応することは可能でしょうか。
たとえば、jquery_inc.htmlに記述するモジュールAの相対パスが../index.htmlとします。
モジュールAを、ことなる階層にある各HTMLでインクルードするとき、index.htmlや../../index.htmlというように、自動的に相対パスの変更を行ってくれればいいなと思いました。
(ローカル環境でもリンク確認を行いたいことが最大の理由です。)

そういう仕様が技術的に可能なのか知りたくて、コメントしてみました。

これからも、こうした痒いところに手が届くプラグインを楽しみにしています。


tinybeans様、
お忙しい中、ご回答いただきありがとうございます。

さっそく挑戦してみましたが、
カレントhtmlから見たjquery_inc.htmlの相対パスを取得させるための、.replace()内の正規表現の作成が、自分の能力の限界を超えていました。

jquery_inc.htmlファイルのパス
例:ルートディレクトリ/common/jquery_inc.html

var path = location.pathname.replace(パス取得のの正規表現不明) + '/';
var inc_url = path + 'jquery_inc.html'; // 代入こんな感じ?にすればよい??


さらに、サーバーによっては、ルート相対パスが必ずしも「ドメイン/ルートディレクトリ」と解釈されるとは限られないことも知りました。(例えば、状況によっては、http://common/~というように、サーバールートからのパスになってしまうこともあるらしいのです。)

あつかましいとは思いますが、インクルードの応用範囲が広がるように、
配布JSでルート相対パス(/root/sub/xxx.html)・相対パスの両方に対応したものを、ぜひ実現してください!
よろしくお願いいたします。

連続で申し訳ありません。
よくよく考えてみましたら、
jquery_inc.html内のインクルードモジュールで相対リンクを使用している場合、ここのパスも適切な相対パスに変換するような制御をしないとだめですよね。

そうなると、かなり高度なコーディングが必要とされるでしょうか。

こうしたインクルードスクリプトが、需要が多そうであるのにあまり配布されていない理由は、相対パスの制御も大きなネックになっているからなのかなぁ、と思いました。

素直にApache入れるしかないかな・・・o(´^`)o

TinyBeans様

phpが使用できずアクセシビリティに有効なインクルードの方法を探していましたところ意図しているものそのままのjQuery_inc.jsを発見し、感動しておりました。
独学で制作していらっしゃるとの事なので目からうろこです。

さっそく使わせていただこうと思いました所、分かりやすく記述していただいているのですがサンプル通り「replace_inc」を実行させようとしてもインクルードした要素と置き換わる事無く、表示されてしまいます。

MTではなく、通常のWEBで使用しようとしておりますので「jquery_inc.js」に他修正など発生したりするものなのでしょうか?

自分でも検索し調べているのですが解決せず、
ご迷惑かと思いましたがどうしても必要でしたので書き込みをさせていただいてしまいました。

重ねて失礼します。

ご参考までにと思いURLを追加しました。
押し付けがましく申し訳在りません。

逆に早いご返信有難うございます!

はい、表層は置き換えられているのですが、
ソースを開いた時に「ここに読み込みます」のままで置き換えられていないのです。

もしかしましたらjQueryのバージョンが新しいとreplace_incが上手く作動しないのかとも思い、旧バージョンのjQueryで試してみたりと色々試行錯誤しているのですけれどやはりソースが置き変わらずでいます。

tinybeans様の方で何か原因などがお分かりになりましたらと思いまして、、、続けてお聞きしてしまってよろしいでしょうか。

分かりやすいご回答有難うございます!!

通常見ていたソースの表示はJavaScriptが実行される前のソース
だと今更ながら知りました。
Firebugで確認しました所、置き換えられていました。
はい、とても有意義なjQueryとして使わせていただきます^^

分かりやすく色々記述されていらっしゃるのでとても参考になりますのでブックマークに登録させていただいて今後も勉強させていただきます。

お忙しい中ご丁寧に有難うございました。

複数箇所にjQueryで読み込むのに
なにか良い方法はないか探して、こちらを見つけました。
希望の処理が全て盛り込まれていて、大変役に立ちました。
ありがとうございます。

1点質問があり、こちらのjsの問題とは別かもしれませんが、
thickboxなど、読み込んだ部分に対して指定したjQueryが機能しませんでした。
初めからある方は機能し、読み込んだ部分のみ機能しません。

これの解決方法はありますでしょうか?

コメントする