0%

last updated 2021.03.12

develop

no-jsにも配慮した最強のwebページローディングを模索する

最終更新日から1年以上過ぎています。
内容が古くなっている可能性もあるのでご注意ください。

ページの要素ロード中のガタガタを隠すために「ローディング画面」を実装するサイトが主流になってきました(当サイト含め)。
単純に実装するだけならばそこまで難しくはなく、少しググれば説明しているサイトはたくさん見つかります。

ただ、そのほとんどが「ページ全体を覆い隠す“loading”要素を作っておいて、要素がすべて読み込まれた段階でjavascriptから非表示にする」という実装で。
これだと、仮にjavascriptが無効な環境でアクセスした場合、ローディングが永遠に終わらずコンテンツを見る事ができない状況に陥ってしまいます。

終わらないぐるぐる

今のご時世でjavascriptを無効にしている環境というのは、もはや切り捨ててもいいような少数派なんでしょうけれど、それにしても「コンテンツをまったく閲覧できない」というのはあまりに…と思ってしまい、なんとかここに配慮できないか模索してみました。

うまくいかなかった方法

結論から言うと最終的にそれっぽいものができたんですが(このサイトにはそれを実装してます)、試したけれどうまくいかなかった方法も紹介しておきます。

html
<!DOCTYPE html>
<html lang="ja">

  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="style.css">
  </head>

  <body>
    <div id="loading">
      <!-- ここにローディングアイコンとか -->
    </div>
    <div id="content">
      <!-- ここにコンテンツ -->
      <script src="jquery.js"></script>
      <script src="script.js"></script>
    </div>
  </body>

</html>
style.css
div#loading {
	position: fixed;
	z-index: 1000;
	background-color: #ffffff; /* お好きなカラーに */
	width:100%;
	height: 100%;
	top:0;
	left: 0;
}
script.js
//jQuery
$(function(){
    $('#loading').css('display','block');
});
$(window).on('load', function() {
    $('#loading').fadeOut();
});

考え方としては、「デフォルトでは“loading”要素は非表示で、DOMが読まれた瞬間にjavascriptから表示し、要素がすべて読み込まれたら非表示にする」というもの。
no-js環境であれば、そもそもloadingを表示する処理も働かないので、普通にコンテンツを見る事ができます。

アイデアは良かったと思うのですが、ダメでした。javascript側からloading要素を表示する処理が走る前に、一瞬コンテンツが表示されてしまうのです。

いわゆる「チラつき」です

これではローディングを実装する意味がありません。没。

うまくいった方法

考え方

javascriptからloading要素の表示処理を行う方法でチラつきが発生するのは、DOMやCSSの読み込みに比べてjavascriptの動作が一瞬遅れるためです。

scriptをbodyの最後で読み込んでいるためかと思い、loadingを表示する処理の部分だけ取り出してheadにインライン配置してみたところ、かなり改善しましたが通信環境によってはまだ安定しない。ということで、もう一工夫。

loading要素は始めから表示しておきつつ、コンマ数秒のdelay後、勝手にフェードアウトするアニメーションを設定しておく。DOMがロードされた瞬間に、headにベタ書きしたjavascriptから即座にそのアニメーションを一時停止し、要素がすべてロードされたらリスタートさせる」というやり方に落ち着きました。

コピペ用コード

こちらがコピペ用コードです。
ローディング周りはサイトによっていろいろと演出に凝りたい部分だと思うので、cssなどは適宜カスタマイズの上お使いください。

html
<!DOCTYPE html>
<html lang="ja">

  <head>
    <meta charset="utf-8">
  <!-- STEP1 DOM読み込みと同時にhtmlにclass「onLoad」を追加 -->
  <!-- (html.onLoad配下のloading要素はanimationがpausedされる) -->
    <script>
      const html = document.getElementsByTagName('html');
      html[0].classList.add('onLoad');
    </script>
    <link rel="stylesheet" href="style.css">
  </head>

  <body>
    <div class="loading">
      <img id="loadingIcon" src="loading.gif">
    </div>
    <div class="content">
      <!-- ここにコンテンツ -->
      <script src="jquery.js"></script>
      <script src="script.js"></script>
    </div>
  </body>

</html>
style.css
div#loading {
	position: fixed;
	z-index: 1000;
	background-color: #ffffff; /* お好きなカラーに */
	width:100%;
	height: 100%;
	top:0;
	left: 0;
	animation: disappear 0.5s ease-in-out 0.5s both;
}
img#loadingIcon {
	display:none;
	position: fixed;
	z-index: 1001;
	top:0;
	left: 0;
}
@keyframes disappear {
	0% {
		display: block;
		opacity: 1;
	}
	99% {
		display: block;
		opacity: 0;
	}
	100% {
		display: none;
		opacity: 0;
	}
}
html.onLoad, html.onLoad body {
	overflow: hidden;
	div#loading {
		animation-play-state: paused;
	}
}
script.js
//jQuery
$(function(){
//STEP2
//1s以上かかった段階でロード完了していなかったらLoadingアイコンを表示
    setTimeout(function(){
    	if($('html').hasClass('onLoad')) {
    		$('#loadingIcon').css('display','block');
    	}
    },1000); 
//STEPα
//5秒間ローディングが完了しなかったら諦めて表示しちゃう
    setTimeout(function(){
    	$('html').removeClass('onLoad');
    },5000); 
});

//STEP3
//ロードが完了したら、ロード終了イベントを起こす
$(window).on('load', function() {
    setTimeout(function(){
    	$('html').removeClass('onLoad');
    	$('div#loading').css('animation-delay','0s'); //delayの残りを省略
    },1); //ieでonloadがフライング実行されるの防止
});

相当特殊な通信環境でないかぎり、コンマ数秒のdelay中にjavascriptのpausedトリガーが発動するはずです。
もし、間に合わなくてローディング要素のフェードアウトが先に始まってしまうことが多い場合は、cssのdelayの秒数を少し長めに調整してあげてください。
要素全ロード完了後に残りdelayをリセットする処理を入れているので、仮にdelayを10秒とかにしても、jsが動く環境であれば必要以上に待たされる危険性はありません。

delayの秒数は「js無効な環境でアクセスした際に待たされる秒数」です。ロードが終わろうが終わるまいがdelay後にコンテンツの表示が始まります

まとめ

「ローカル環境や開発用の高スペックパソコン、あとは何度も表示してるとキャッシュが溜まって一瞬で終了してしまって検証が難しい」のがローディング実装の最大の難関だと思ってます。
このやり方も繰り返してるうちにいろいろと粗が見つかるのかもしれません。適宜ブラッシュアップしていこうと思っています。

recommend

develop / 2021.05.09

svgの縦横比を可変にする
svg