ぶれすとつーる

だいたいjavascript

DOMContentLoadedとスタイルシートの読込み

DOMContentLoadedの発火とスタイルシートの読み込みは間接的に関係あるらしい

MDNのDOMContentLoadedのページみてたら

The DOMContentLoaded event is fired when the document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading (the load event can be used to detect a fully-loaded page). Note: Stylesheet loads block script execution, so if you have a <script> after a <link rel="stylesheet" ...>, the page will not finish parsing - and DOMContentLoaded will not fire - until the stylesheet is loaded.

と書いてある(詳細:a-styles-sheet-that-is-blocking-scripts).

DOMContentLoadedイベントはドキュメントの読み込みと解析が完了したらスタイルシートの読み込みや画像の読み込み、フレームの読み込みの完了をまたずに発火します。

(注意) とはいえスタイルシートの読み込みはscriptの実行をブロックするので、もしあなたが<script><link rel="stylesheet" ...>の後ろにいれてたら、ページの読み込み完了やDOMContentLoadedの発火はスタイルシートのロードが終わるまで行われません。

多分こんな感じ。

検証してみた

スタイルシートがscriptの前にあるケース(ブロックされる)

サーバサイドはこんな感じでcssの読み込みだけ3秒(3000ms)ほど遅らせる

var static = require('node-static')
  , fileServer = new static.Server('./public/')
  , http = require('http')
  , path = require('path')
  ;

http.createServer(function (req, res) {
  req.addListener('end', function () {
    var url = req.url
      , ext = path.extname(url)
      ;

    if (ext === '.css') { //cssは遅らせる
      setTimeout(function () {
        fileServer.serve(req, res);
      }, 3000);
    } else {
      fileServer.serve(req, res);
    }
  }).resume();
}).listen(3010);

検証するページはこんな感じ (DOMContentLoadedが発生したらparseStartからの経過時間(ms)を出力)

public/index.html

<html>
<head>
  <script type="text/javascript">
  var parseStart = Date.now();
  (function (doc) {
    doc.addEventListener('DOMContentLoaded', function () {
      var onDomContentLoaded = Date.now()
        , elapsed = onDomContentLoaded - parseStart
        ;

      doc.body.innerHTML = doc.body.innerHTML + "<br>fire DOMContentLoaded " + elapsed;
    }, false);
  }(document));
  </script>
  <!-- scriptの前にスタイルシートが存在する(scriptの実行をブロックするはず) -->
  <link rel="stylesheet" href="./css/foo.css" type="text/css">
  <!-- js(パーススタートから実行までの経過時間を(ms)出力) -->
  <script type="text/javascript">
  document.write('exec foo.js: ' + (Date.now() - parseStart) + '<br>');
  </script>
</head>
<body>
hello world
</body>
</html>

出力結果

exec foo.js: 3005
hello world
fire DOMContentLoaded 3005

f:id:nazomikan:20140202190541p:plain

(青線がDOMContentLoaded, 赤がonload)

スタイルシートの読み込み(3000ms)とjsの実行を待ってからDOMContentLoadedが発生しています

スタイルシートがscriptの後ろにあるケース(ブロックされない)

次にscriptとlinkの順序を入れ替えてみます

public/index.html

<html>
<head>
  <script type="text/javascript">
  var parseStart = Date.now();
  (function (doc) {
    doc.addEventListener('DOMContentLoaded', function () {
      var onDomContentLoaded = Date.now()
        , elapsed = onDomContentLoaded - parseStart
        ;

      doc.body.innerHTML = doc.body.innerHTML + "<br>fire DOMContentLoaded " + elapsed;
    }, false);
  }(document));
  </script>
  <!-- 順番入れ替えた、scriptの後ろにスタイルシートあるので-->
  <!-- DOMContentLoadedはcssの読み込みを待たずに実行するはず -->
  <script type="text/javascript">
  document.write('exec foo.js: ' + (Date.now() - parseStart) + '<br>');
  </script>
  <link rel="stylesheet" href="./css/foo.css" type="text/css">
</head>
<body>
hello world
</body>
</html>

出力結果

exec foo.js: 0
hello world
fire DOMContentLoaded 1

f:id:nazomikan:20140202190712p:plain

(青線がDOMContentLoaded, 赤がonload)

表示されてから3秒後にスタイルがあたった感じになりました。

まとめ

スタイルシートそのものはDOMContentLoadedと直接関係しないけどscriptの実行が遅延させられることで間接的にDOMContentLoadedを遅らせる結果になる。

なのでDOMContentLoaded後に要素の座標計算するようなJSを書いてる場合、スタイルシートがscriptタグより後ろにある場合はDOMContentLoadedがスタイルシートの読み込みをまたないので、想定した(cssによって決定される)座標と違う座標がとれたりして死ぬので注意しないといけませんね。

(scriptの後ろにスタイルシート読み込んでるケースなんてほとんどみたことないけど)