TL;DR
外部 CSS を読み込んだら、何かしらスクリプトを走らせよう。
前置き
外部スタイルシートに CSS transition を記述した際、仕様上は初期状態への遷移は発生しないはずなのだけれど、Chrome だと、デフォルトのスタイルから初期状態への遷移が発生してしまう、という問題。
かなり以前から知られていた問題らしいのだけれど、未だに直っておらず、それぞれに対処法は考えられているものの、なぜそれで動くのかよくわからなかったので、少し追っかけてみた。
結果、全容を把握するに至らなかったのだけれど、おおよその機序と回避方法が分かったので、メモがてらに残しておく。
問題の発生する例
例えば、こんなスタイルシートがあって、
.anim {
background-color: red;
transition: background-color 5s ease-out 0s;
}
.anim:hover {
background-color: blue;
}
こんな html を書く。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" href="hoge.css">
</head>
<body>
<p class="anim">hoge</p>
</body>
</html>
最初は赤背景で、マウスオーバーしたら青に……というのを期待するのだけれど、
Chrome で実行すると、ページ読み込み時にデフォルト(透過)から赤への遷移アニメーションが挟まってしまう。
機序
どうやら Chrome さんは、外部スタイルシートを遅延解決しようとしているようで、
上記のような記述だとスタイルの適用が後回しにされる。
それが初期 DOM 構築後になると、新規にクラスが設定されたとして、遷移アニメーションが発火してしまう。
対処
初期 DOM 構築完了前に、外部スタイルシートを解決させる。
具体的には何かしらスクリプトを動かすだけで良い。
スクリプト実行前に、外部スタイルシートの解決を行ってくれる。
例えば以下のように。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" href="hoge.css">
</head>
<body>
<p class="anim">hoge</p>
<script>console.log("ちゃんとスタイル適用してね。");</script>
</body>
</html>
もちろん、link
の直後でも良い。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" href="hoge.css">
<script>console.log("ちゃんとスタイル適用してね。");</script>
</head>
<body>
<p class="anim">hoge</p>
</body>
</html>
外部スタイルシートが複数あることを考えると、前者の body
の最後に入れてしまう方法が汎用的でよい気がする。
なお、script
要素の中身は何でもいいが、空にするのはダメ(単にスキップされる)。
空白のみ、はアリだけれど、module bundler や minifier に消されてしまう可能性があるので注意。
追記: script
要素は外部スクリプトの読み込みでも大丈夫なようです。(コメント欄参照)
補遺
link
は script
の前に置け、という対処方法が紹介されることもあった(し、誤りではない)けれど、link
の前に script
があること自体が問題を起こしているわけではない。
link
の後に何かしら中身のある script
を置く、ということが回避策になるようだ。
先人たち
https://qiita.com/yaegaki/items/46209320e7163cb638a9
https://intp.jp/?p=796
https://css-tricks.com/transitions-only-after-page-load/