画像の遅延読み込み
Tweet
yamaokaです。
webページの表示を高速化する手法にはいろいろありますが、 その一つとして遅延読み込みという手法があります。 初期状態で表示する必要のない要素については読み込まず、 必要になったタイミングで読み込み、表示するようにする手法です。
ページの読み込みにかかる時間の大半を 画像の読み込みが占めている場合が多いので、 画像の読み込みを遅延させるという手法が多く取られます。
検討するべきケース
では、画像の遅延読み込みはどのような場合に検討されるべきでしょうか。 最初から表示されている必要がない画像が存在し、その画像のサイズが大きかったり、 そうした画像の数が多い場合は検討してみる価値があると思います。
例えば、次のようなケースです。
- 初期状態では表示されないブロックに属する画像が存在し、 JavaScriptで表示するかしないかを切り替えているような場合
- ページのずーっと下の方に画像があって、 その画像は画面をスクロールしないと見えないような場合
仕組み
では、どのように画像の遅延読み込みを実現すればよいのでしょうか。 仕組みは単純です。
- 適当なタイミングで、読み込みたくない画像のimg要素のsrc属性の値を適当な画像(1ピクセル四方のGIF画像など)のパスに置き換える
- 表示が必要になったタイミングで、先ほど置き換えたsrc属性の値を元に戻し、実際の画像を読み込んで表示させる
これだけ。簡単ですね。
一番目の「適当なタイミング」とは、 ページ全体のDOMツリーの読み込みが完了したタイミングがいいでしょう。 そのタイミングで画像のimg要素のsrc属性を書き換えてしまうことにより、 その時点では不要な画像の読み込みを抑えることができます。 場合によっては、画像が属するブロックの読み込みが完了したタイミングで 実行してしまってもいいかもしれません。
二番目の「表示が必要になったタイミング」とは、 表示されていなかった要素が表示されるタイミングとか、 画面がスクロールされてきたとかそういうタイミングを指します。 そのイベントを検知して、画像を表示させればよいのです。
具体例
具体例として、idが「foo」のブロック要素に含まれる画像を遅延読み込みさせる場合を考えてみます。 prototype.jsをライブラリとして利用している場合、 次のように遅延読み込み処理を記述することができます (便宜上、「Foo」を名前空間代わりに使用しました)。
Foo = {};
Foo.images = [];
Foo.stopLoadingImages = function() {
$$("#foo img").each(function(image) {
image.orgSrc = image.src;
image.src = "1x1.gif"; // 小さな画像に置き換え
Foo.images.push(image); // 置き換えた要素をとっておく
});
};
Foo.loadImages = function() {
Foo.images.each(function(image) {
image.src = image.orgSrc; // 元の値に戻す
});
};
あとは、「Foo.stopLoadingImages」と「Foo.loadImages」を 必要なタイミングで実行するようにするだけです。
ライブラリの紹介
実際には、何らかのライブラリを利用して遅延読み込みを実現する場合が多いでしょう。 主なライブラリとして、次のようなものがあります。
-
YUI ImageLoader
YUI(Yahoo! UI Library)の一部として開発されているライブラリです。 -
Lazy Load Plugin for jQuery
jQueryのプラグインとして開発されているライブラリです。 -
lazierLoad - Javascript Image Lazy Loader for Prototype
prototype.jsの利用を前提としたライブラリです。
それぞれのライブラリでできることに違いがありますので、 既に自分が使っているライブラリとの適合性も含め、一度見てみるといいかもしれません。
まとめ
体感速度を少しでも速くするための手法として、画像の遅延読み込みは効果的です。 初期状態では画像に通常のimg要素が用いられるため、 JavaScriptが有効になっていない環境で閲覧された場合でも、何の副作用もありません。 必要に応じて導入を検討してみるといいのではないでしょうか。
2008年1月18日12:50追記
「仕組み」の「適当なタイミング」について加筆訂正を行いました。

コメント
これは使えそうですね!
ただライブラリ?によっては印刷プレビューをすると読み込んでない画像の部分は当然、印刷プレビューでも表示されないままですね。
印刷するとなると一度全部スクロールさせて読み込ませないといけないわけですね。
投稿者: Syo | 2008年1月18日 12:04
確かに、スクロールしないと見えない部分の画像を遅延読み込みさせる場合、印刷する場合をきちんと考慮しないといけませんね。印刷用の画面を別途用意するなど、何らかの対策が必要かもしれません。
投稿者: yamaoka | 2008年1月18日 23:40
はじめまして。
わたくし、javascriptについてかなりの初心者なのですが、遅延表示の仕組みのページを作ろうと考えております。
20枚近くある画像を遅延表示させようとおもっているのですが、それぞれの画像名はFoo関数のどのあたりに入れるのでしょうか?
また表示非表示実行の関数呼び出しは、~~~~ = Foo.stopLoadingImages()や
~~~ = Foo.loadImages() でしょうか?
そうであれば画像名は()の中に入れるのでしょうか?
不躾な文章で申し訳ありません。
教えて頂けるととても嬉しいです^^;
投稿者: yoshi | 2008年1月19日 04:26
何度も申し訳ありません。
Foo = {};
Foo.images = [];
Foo.stopLoadingImages = function() {
$$("#foo img").each(function(image) {
image.orgSrc = image.src;
image.src = "1x1.gif"; // 小さな画像に置き換え
Foo.images.push(image); // 置き換えた要素をとっておく
});
};
Foo.loadImages = function() {
Foo.images.each(function(image) {
image.src = image.orgSrc; // 元の値に戻す
});
};
↑
はheadタグ内に書くのでしょうか?
超初心者の質問で誠に申し訳ありません。
投稿者: Anonymous | 2008年1月19日 04:30
記述した例で言うと、idに「foo」と付いている要素に含まれるimg要素が全て遅延読み込みの対処となります。例えば次のようなブロックがある場合、xxxとyyyとzzzは全て遅延読み込みの対象になります(この辺詳しくはprototype.jsの$$関数を調べてみてください)。
<div id="foo">
<img src="xxx" />
<img src="yyy" />
<img src="zzz" />
</div>
関数の記述位置はhead内でも、別ファイルとして作成してscriptタグで読み込んでもかまいません。
読み込み抑止の関数は次のように、
Foo.stopLoadingImages();
また、読み込み開始して表示する関数は次のように記述できます。
Foo.loadImages();
それぞれ任意のタイミングで実行するようにしてください。
(「Foo」という名前はとりあえず例として付けてあるだけですので、何か意味のある名前に付け替えることをオススメします…)
投稿者: yamaoka | 2008年1月21日 11:33
お返事とご指導ありがとうございます。
非常に丁寧でわかりやすいです。
申し訳ありませんがあとお一つお聞かせください。
読み込み抑止と読み込み開始の関数の呼び出し時の画像ファイルの指定は、
どのようにするのでしょうか?
Foo.stopLoadingImages();や
Foo.loadImages();の
()の中に画像名を入れるのでしょうか?
本当に何度も申し訳ありませんm(__)m
投稿者: Anonymous | 2008年1月22日 14:59
画像名を指定する必要はありません。示したコード例で言うと、
<div id="foo">
</div>
で囲まれた部分に含まれる全ての画像が遅延読み込みの対象になります。
詳しくはprototype.jsの$$関数のドキュメントをご覧ください。
http://www.prototypejs.org/api/utility#method-$$
投稿者: yamaoka | 2008年1月22日 15:10
解決しました。
初心者の質問に対し丁寧に教えていただきまして本当にありがとうございます!^^
投稿者: Anonymous | 2008年1月24日 08:13
初めまして、いつも楽しく拝読しております。
この遅延読込ですが、iframe要素にも効きますか?というのも、ブログパーツとしてのAmazonなどのアフィリエイトデータって、iframeを使っているものが多いですが、サイト上で利用しているjsデータはAJAXで無い限り全てのHTMLデータを読み込んだ後で効果が出たりしますよね(ex. curvyCorners)。
そこで一旦別要素としてそのiframe部を読み込んでページを成立させてから、改めてその部分のiframeを載せる、というのは出来るんでしょうか。
今回の方法がそれに非常に近いなと思い、質問させていただきました。
どうぞよろしくお願い致します。
投稿者: にのみや | 2008年1月29日 19:54
遅延読み込みの技術はimg要素以外でも有効だと思います。iframe要素でもできると思うのでぜひ試してみてください。
投稿者: yamaoka | 2008年1月30日 14:50