2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

株式会社ネオシステムAdvent Calendar 2024

Day 6

CSSの疑似クラスhasを使い、任意の要素の有無でスタイルの制御をしたい

Last updated at Posted at 2024-12-05

やったこと

class属性で状態用のclassをJavaScriptで付けたり外したりすることで、CSSでclassの有無で見た目を切り替えるという事をよくやっています。

そこで、ある要素Xが存在する場合にある要素Yの表示を切り替えたいと思いました。今どきはJavaScriptのフレームワークでコントロールしてしまうとは思いますが(Vueのv-showなど)

調べましたが、サンプルが子要素か兄弟要素を使うモノしか見当たらず悩んで試行錯誤してました。

結果、body:has(要素Xのセレクタ) 要素Yのセレクタとすればできたので書き残しておきます。

デモ

次のデモでは5秒毎に#target要素(要素X)を交互に追加・削除しており、これがDOM上にあるかどうかで、#view要素(要素Y)のスタイルを変える、という感じになっています。chrome/firefox/safari(iOS)で確認。

Demo: https://jsbin.com/haleqeqiji/2 (コード

/* "ある"ときも"ない"ときも適用する */
#view::before {
  content: '表示されることは無い';
  font-weight: bold;
}

/* #target が "ある" ときに適用する */
body:has(#target) #view::before {
  content: 'あるよ';
  color: green;
}

/* #target が "ない" ときに適用する */
body:not(:has(#target)) #view::before {
  content: 'ないよ';
  color: red;
}

(デモでは::beforeという疑似要素を使っていますが、任意のセレクタで大丈夫です)

解説

なぜ機能するか?

body:has(セレクタ) とすることで、bodyから見てセレクタを満たせるか、という感じになります。
Pasted image 20241204040508.png

body:not(:has(セレクタ))は逆で、bodyから見てセレクタを満たせないか、という感じになります。
Pasted image 20241204040525.png

body:has(セレクタ)もしくはbody:not(:has(セレクタ))が満たされると、bodyが対象要素(Subject element)となります。

今回の例では、body:has(セレクタ) #viewもしくはbody:not(:has(セレクタ)) #viewとしているので、bodyの子孫要素である #viewが対象要素となるので、狙った通りうまくいく、という感じです。

難点はCSSセレクタの意図がわかりにくいところでしょうか。

bodyでないといけないのか?

「有無を見たい要素(今回は:has(セレクタ))」と「対象要素(今回は#view)」が同じ子(子孫)に存在するならbodyでなくても機能します。

Demo: https://jsbin.com/nayucazopi/3/ (コード)

しかしbodyなら必ず親(先祖)にいる要素なので確実な気はします。

bodyは省略できないのか?

bodyはコンテンツ上一番上にある要素なので、省略したいところですが、:hasから始めるやり方はうまく機能しません。
困ったことに、ちょっとうまく動いてしまうという事があります。

具体的には、"ある"時だけを考えたときは省略しても動いているように見えてしまう、というのがあります。

/* "ある"ときも"ない"ときも適用する */
#view::before {
  content: 'ない?';
  font-weight: bold;
}

/*  #target が "ある" ときに適用する */
:has(#target) #view::before {
  content: 'あるよ';
  color: green;
}

"ある"ときだけ適用するのはそれっぽく見えます。

image.png

image.png

しかし、"ない"時に適用しようとすると、この書き方はうまくいきません。

/* "ある"ときも"ない"ときも適用する */
#view::before {
  content: 'ある?';
  font-weight: bold;
}

/* #target が "ない" ときに適用したかった */
:not(:has(#target)) #view::before {
  content: 'ないよ';
  color: red;
}

image.png

#targetの要素があるのに、:not(:has(#target)) が適用されてしまい、意図しない表示となってしまいました。難しい。

参考

(:hoverと組み合わせているサンプルはどういう考え方をすれば身に付くのだろう…)

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?