JavaScript

ページをフワッとフェードインさせる

タイトル通り、ウェブページを読み込んだ時にふわっとフェードインで表示させるやり方です。フェードインは、CSSだけで再現するのであればanimationでできますが、今回のはjQueryの話になります。たまたま jQuery でフェードインするのに「CSS で body を非表示にしておきます」みたいなスクリプト無効環境完全無視みたいな記事を見てしまったので一応メモします。

一応、自分なりに以下の条件を考慮しているのですが、すべてをクリアするとちょっとややこしいです。

  1. フェードイン前にページが一瞬でも表示されるのを防ぐ
  2. スクリプトが機能しない環境でページが非表示になるのを避ける
  3. document.write()は使わない
  4. ローディング表示も出来るようにする。
  5. 画像などの読み込みも終了してから表示する。
  6. jQueryにページのレンダリングをブロックさせない。

まずjQueryの方、フェードインは.fadeIn()、フェードアウトは.fadeOut()を使います。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<script>
$(document).ready(function() {
  $('body').hide().fadeIn(2000);
});
</script>

.fadeIn()|.fadeOut()は第一引数でフェードの経過時間(デュレーション)をミリ秒(例:2000 = 2秒)で指定します。経過時間にはミリ秒の他に'fast''slow'のキーワードが使用できます。フェード後のコールバックは第二引数に指定します。

イージングやエラー処理など細かいオプションを指定する場合は第一引数にオプションのオブジェクトを渡します。詳しくは fadeIn() | jQuery API Documentationを参照してもらうといいです。


条件1と2、
フェードインするためにはブラウザがページを表示しようとする前にページを隠しておく必要があります。DOMのロードが完了$(document).ready()してからの.hide()では、場合によっては一瞬だけページ内容が表示されてしまうことがあります。

CSSで予めページを非表示にしておくと大丈夫ですが、スクリプトが機能しない環境でページが見れないような方法は避けないといけないので、せめて<head>内に<noscript>で上書きするくらいはしておきます。

<style>body { display: none; } /* フェードイン用 */</style>
<noscript>
<style>body { display: block }</style>
</noscript>

条件3、document.write()は、DOMとパフォーマンスに関わるなんやらで推奨されていません。カンタンに言えば古い書き方です。
3.3 APIs in HTML documents › 3.5.3 document.write() — HTML5

<head>
<script>
document.write( '<style>body { display: none }</style>' );
</script>
</head>

条件4、
後からでも簡単にローディングの表示が出来るような汎用性というか拡張性も欲しいです。そもそもですが、フェードインに関わらず表示や配置に関わるエフェクトは、<body>自体ではなくページ全体をラップした<div>を用意したほうがCSSの困りごとが減ります。こうしたラッパーがあると、ライトボックスやスクロールボタン、外部サービスによって挿入される何がしか、などスクリプトによって後から<body>直下に追加される要素を分けて管理しやすくなります。

なので、元から<body>ではなくラッパーをフェードインするか、ラッパーに重なったローディングのレイヤーをフェードアウトする設計の方が色々便利です。ラッパーはページ全体のサイズになるようCSSで初期設定をしておきます。古いIE対策するなら高さを100%にする必要があります。

body { margin: 0; padding: 0; width: 100%; max-width: 100%;  height: 100%; min-height: 100%; }

/* ラッパー */
#wrapper { margin: 0; padding: 0; width: 100%; max-width: 100%; height: auto; min-height: 100%; display: block; overflow: hidden; position: relative; z-index: 1; }
  /* 古いIE対策するなら */
  #wrapper { height: 100%; overflow: visible; }
  html > body #wrapper { height: auto; overflow: hidden; }

/* ローディング */
#loading { background: #fff; margin: 0; padding: 0; width: 100%; height: 100%; position: fixed; top: 0; left: 0; z-index: 100; overflow: hidden; }
<body>
  <div id="loading"></div>
  <div id="wrapper">
〜ページの内容〜
  </div>
</body>

条件5、
$(document).ready()でフェードするよりwindow.onloadのタイミングの方が遅いけどキレイです、というだけです。高さが指定されていない大きな画像とか描画の様子が見えてしまう場合でも、ロードイベントならスムーズな表示になると思います。

$(window).on('load', function(){
  $('#loading').fadeOut(2000, function(){
    $(this).remove();
  });
});

条件6、
<head>内で読み込む外部スクリプトや大きなインラインスクリプトはページのレンダリングをブロックします。基本的には<head>内や<body>の上部は必要最低限の短いインラインのみにして、その他のスクリプトは<body>下部に設置するようにします。GoogleのPageSpeed Insightsで高得点を叩き出すのであればjQueryの読み込みも一工夫必要ですがそれはまた別の記事にでも書きます。


というわけであんなにも簡単だったはずの「ページをフワッとフェードイン」がこうなっちゃいました。

<html>
<head>
<style>
body, #wrapper { margin: 0; padding: 0; width: 100%; max-width: 100%; }
body { height: 100%; min-height: 100%; }
#wrapper { height: auto; min-height: 100%; display: block; overflow: hidden; position: relative; z-index: 1; }
#loading { background: #fff; margin: 0; padding: 0; width: 100%; height: 100%; position: fixed; top: 0; left: 0; z-index: 100; overflow: hidden; }
</style>
</head>
<body><div id="wrapper">
<script>
window.d = document;
var loading = d.createElement('div');
    loading.id = 'loading';
var wrapper = d.getElementById('wrapper');
    wrapper.style.visibility = 'hidden';
    wrapper.parentNode.insertBefore(loading, wrapper);
</script>

〜ページの内容〜

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<script>
$(window).on('load', function(){
  if ( !window.loading || !window.wrapper ) return;
  wrapper.style.visibility = 'visible';
  $('#loading').fadeOut(2000, function(){
    $(this).remove();
  });
});
</script>
</div></body>
</html>

レンダリングが始まる前にページを隠すレイヤー<div id="loading">をラッパーの手前に作って、ページ読み込み完了後にfadeOut()します。<div id="loading">にクルクル回る背景画像を指定すればローディング画面になります。

<div id="loading">を作っている場所はdocument.write()を使わず、且つ、ページのレンダリングが始まるよりも前、且つ、<div id="wrapper">を操作できる場所、ということになりますが、もっと他にいい案あればどなたか教えて下さい。

<body>を直接フェードインしていて「ページによってはすごく遅い」なんてことがある場合には、ローディングレイヤーを用意してフェードアウトする方法も試してみてください。