制約のない :has() のパフォーマンス計測

CSS の :has() が全ブラウザで使えるようになって久しいですが、その特性上パフォーマンス上の問題には注意したいところです。

とくに *:has(.foo) のように全称セレクターを使うなどして制約のない要素を対象にした場合は顕著にパフォーマンスが悪化するので、できるだけ避けるべきでしょう。このような「制約のない :has()」はたとえば以下のパターンがあります。

  • :has(.foo)
  • *:has(.foo)
  • .bar:has(*)

これらのセレクターを使用すると、Firefox のインスペクターでは亀のアイコンによる警告メッセージが表示されるのですが、わざわざインスペクターを開き当該要素を選択する必要があるため、なかなか気付きにくいものと思います。

サムネイル画像
Firefox のインスペクターで :has(.foo) を選択し、ツールチップでこのセレクターは制約のない :has() を使用しているため動作が遅くなりますの警告文が表示された様子オリジナル画像

こういうのは lint ツールで検出できれば良かろうと Stylelint に Issue を挙げており(GitHub)、提案は好意的に受け止められて次期メジャーアップデートに入る可能性はある(GitHub)のですが、まだ未定です。

  • この提案に賛同いただける方は当該 Issue にアクション頂けると私が喜びます。

そんな折、Mastodon の投稿(indieweb.social)で知ったのですが、Chrome など Blink 系ブラウザには CSS セレクターのパフォーマンス分析機能があり、ページ内で使用されているすべてのセレクターの統計を見ることができるようになっていたようです。

この一覧表を「Elapsed(経過時間)」でソートさせれば CSS セレクターの照合が遅い箇所が一目瞭然ですし、具体的にどの程度遅いかも分かって便利ですね。

サムネイル画像
Chrome で本ブログのトップページを開き、DevTools の「パフォーマンス」パネル内の「セレクタの統計データ」タブを表示した様子、:has(> .c-entry-link) が 0.751 秒で2位の3倍以上遅いオリジナル画像