🐭

[CSS] ホバーした要素以外の兄弟要素の透過率(opacity)を下げる方法

に公開

背景

「ある要素にマウスオーバーした際、その兄弟要素の透明度を下げて強調表示する」といったデザインがjQuery全盛期にはよく見られた。

// イメージ
$("a")
  .on("mouseenter", function () {
    $(this).siblings("a").css("opacity", 0.8);
  })
  .on("mouseleave", function () {
    $(this).siblings("a").css("opacity", "");
  });

デザインの流行り廃りやjQueryでの実装もなくなってきて、こういったデザインはあまり見られなくなった。

JavaScript

これをJavaScript(TypeScript)で実装すると結構たいへん。

document.querySelectorAll("a").forEach((anchor) => {
  anchor.addEventListener("mouseenter", function () {
    const parent = anchor.parentElement;
    if (!parent) return;

    const siblings = Array.from(parent.children).filter(
      (el): el is HTMLAnchorElement => el.tagName === "A" && el !== anchor,
    );

    siblings.forEach((sib) => {
      sib.style.opacity = "0.8";
    });
  });

  anchor.addEventListener("mouseleave", function () {
    const parent = anchor.parentElement;
    if (!parent) return;

    const siblings = Array.from(parent.children).filter(
      (el): el is HTMLAnchorElement => el.tagName === "A" && el !== anchor,
    );

    siblings.forEach((sib) => {
      sib.style.opacity = "";
    });
  });
});

CSSで実装する

これまでCSSだけでは実現が難しかったが、:has()の登場で、:not()と組み合わせて以下のような形で実装が出来る。

a {
  transition: opacity 0.2s;
}

:has(> a:hover) > a:not(:hover) {
  opacity: 0.2;
}

/* aではなくクラス名でもOK */

直下のaタグからホバーされていないaだけがopacity: 0.2となる。ホバー中のaタグはopacity: 1のままである。
:has()を使うことで「ホバーされている要素が存在する親要素」を特定することが出来、その中の「ホバーされていない兄弟a要素」に限定してスタイルを適用できる。

現在は兄弟要素のフェードアウト効果は、:has():not(:hover)を組み合わせてCSSのみで実装できる。

Discussion