has
子孫や後ろの兄弟要素の状態をもとに、要素を選べる疑似クラス。 JavaScriptでクラスを付け外ししていた「親を選ぶ」処理を、CSSだけで書けます。
今の書き方
チェックされたチェックボックスを含むかどうかで、行にスタイルを当てられます。 状態が変わればスタイルも自動で切り替わるため、JavaScriptは要りません。
tr:has(input[type="checkbox"]:checked) {
background-color: #fef3c7;
}以前の書き方
親要素を選ぶセレクターがなかったため、強調用のクラス(.is-checked)にスタイルを当てていました。
クラスは、JavaScriptでチェックボックスの変化を受け取り、行に付け外しします。
読み込んだ時点でチェックされている場合に備えて、最初の状態にも合わせておく必要があります。
tr.is-checked {
background-color: #fef3c7;
}
for (const checkbox of document.querySelectorAll('tr input[type="checkbox"]')) {
const row = checkbox.closest('tr')
const update = () => row.classList.toggle('is-checked', checkbox.checked)
update()
checkbox.addEventListener('change', update)
}置き換えるときの注意点
:has()の中に:has()は書けません。:has()の中に疑似要素(::beforeなど)は書けません。:has()の中のセレクターが1つでも無効だと、:has()全体が無効になります。 書き間違いや未対応のセレクターを混ぜると、ルールごと効かなくなります。:has()は、子孫の状態が変わるたびに判定し直されます。body:has(...)のように広い範囲を対象にすると判定の範囲も広がるため、できるだけ近い親要素に絞ります。
フォールバック
対象の5ブラウザはすべて対応済みなので、通常は古い書き方を残す必要はありません。
古いブラウザも支える必要がある場合は、以前の書き方のCSSと今の書き方のCSSを、別々のルールとして両方残します。
1つのセレクターリストにまとめると、:has()に対応していないブラウザではルール全体が無視されます。
JavaScriptは、:has()に対応していないブラウザでだけ動かします。
// :has() に対応していないブラウザでだけ、以前の書き方のクラスを付ける
if (!CSS.supports('selector(:has(*))')) {
const checkboxes = document.querySelectorAll('tr input[type="checkbox"]')
for (const checkbox of checkboxes) {
const row = checkbox.closest('tr')
const update = () => row.classList.toggle('is-checked', checkbox.checked)
update()
checkbox.addEventListener('change', update)
}
}Browser Support
Chrome
v105+2022-09-02Edge
v105+2022-09-01Safari
v15.4+2022-03-14Safari (iOS)
v15.4+2022-03-14Firefox
v121+2023-12-19