HTML
CSS
JavaScript

CSSやJSの読み方次第でgetComputedStyle()で想定通りのスタイルの値が取れないことがある

以下のような挙動があって混乱したのでメモ。

ファイル構成

index.html
<link rel="stylesheet" href="main.css">
<script src="main.js" async></script>
main.css
@import url("external.css");

html {
  font-size: 62.5%;
}
external.css
/* empty file */
main.js
console.log(getComputedStyle(document.body).fontSize)

setTimeout(function () {
  console.log(getComputedStyle(document.body).fontSize)
}, 500)

コンソールへの出力結果

想定した出力

10px
10px

実際の出力

16px
10px

つまり…

最初のconsole.log()時に以下のスタイルが未適用の状態になっているっぽい。

html {
  font-size: 62.5%;
}

回避方法

追記:実際には回避できてないかもしれないので↓のコメントも参照。

1. @importを使わずlinkタグで読み込む

index.html
+<link rel="stylesheet" href="external.css">
 <link rel="stylesheet" href="main.css">
 <script src="main.js" async></script>
main.css
-@import url("external.css");
-
 html {
   font-size: 62.5%;
 }

2. linkタグを使わずstyleでHTML内に直接書く

index.html
+<style>
+@import url("external.css");
+
+html {
+  font-size: 62.5%;
+}
+</style>
 <script src="main.js" async></script>
main.css
-@import url("external.css");
-
-html {
-  font-size: 62.5%;
-}

3. scriptタグのasync属性を使わずコード内でDOMContentLoadedイベントを待つ

index.html
 <link rel="stylesheet" href="main.css">
-<script src="main.js" async></script>
+<script src="main.js"></script>
main.js
+document.addEventListener('DOMContentLoaded', function () {
+
 console.log(getComputedStyle(document.body).fontSize)

 setTimeout(function () {
   console.log(getComputedStyle(document.body).fontSize)
 }, 500)
+
+})
  • diffを見やすくするためインデントは省略。

4. scriptタグ内にJSを書く

index.html
 <link rel="stylesheet" href="main.css">
-<script src="main.js" async></script>
+<script>
+document.addEventListener('DOMContentLoaded', function () {
+  console.log(getComputedStyle(document.body).fontSize)
+
+  setTimeout(function () {
+    console.log(getComputedStyle(document.body).fontSize)
+  }, 500)
+})
+</script>
main.js
-console.log(getComputedStyle(document.body).fontSize)
-
-setTimeout(function () {
-  console.log(getComputedStyle(document.body).fontSize)
-}, 500)
  • 以下のようにbody後にscriptを配置してDOMContentLoadedを使わない形も可。
index.html
 <link rel="stylesheet" href="main.css">
-<script src="main.js" async></script>
+<body></body>
+<script>
+console.log(getComputedStyle(document.body).fontSize)
+
+setTimeout(function () {
+  console.log(getComputedStyle(document.body).fontSize)
+}, 500)
+</script>

5. 何もしないscriptタグを挿入する

index.html
 <link rel="stylesheet" href="main.css">
+<script> </script>
 <script src="main.js" async></script>
  • <script></script>ではダメで、スペースを入れる必要がある。

6. @importを後ろに持ってくる

main.css
-@import url("external.css");
-
 html {
   font-size: 62.5%;
 }
+
+@import url("external.css");
  • 当然これによって評価順序が変わるので注意。

感想

難しいなぁ。なんかバグっぽいと思ったけど、Chrome、Firefox、Safariで同じ挙動だったので、おそらく仕様で決まっているのかも。