はじめに

こんにちは、Rerurate_514と申します。

CSSで閉じるアニメーション作りたいなーって思うことあると思います。
作り方を解説していこうと思います。

ちなみに完成形はこれ

See the Pen Untitled by Rerurate_514 (@Rerurate514) on CodePen.

作り方

まず、閉じる要素と閉じる/開くボタンを用意します。
今回はcontentが最初から開かれた状態で、ボタンを押したら閉じるようにします。

<div class="wrapper">
  <button id="menu-btn">Button</button>
  <div class="menu">
    <div class="content">
      <h1>Hello World</h1>
    </div>
  </div>
</div>

cssはこれ
分かりやすくborderを付けてみました。

.wrapper {
  border: 1px solid red;
}
 
.content {
  transition: 0.3s ease;
  max-height: 100px;
  overflow: hidden;
  
  &.collapsed {
    max-height: 0;
  }
}

jsはこれ

function toggleMenu() {
  const content = document.querySelector(".content")
  content.classList.toggle("collapsed")
}
 
const button = document.querySelector("#menu-btn")
button.addEventListener("click", toggleMenu)

原理

html

<div class="wrapper">
  <button id="menu-btn">Button</button>
  <div class="menu">
    <div class="content">
      <h1>Hello World</h1>
    </div>
  </div>
</div>

menu要素とcontent要素は同じ階層だといい感じになる。

css(scss)

cssは開いたり閉じたりしたい要素に対して、以下の三つを定義する。

  • transition: アニメーション時間 アニメーションの種類(easeなど)
  • max-height: 開いた時の大きさ(固定値のみ)
  • overflow: hidden;

閉じたときのクラスの中に大きさを定義する。

  • max-height: 0;
    上で言うとこれ
.content {
  ...
  
  &.collapsed {
    max-height: 0;
  }
}

&はscssの記法でそのネストの親のクラスが同時に付与されているときのみ適用される。
この場合はclass="content collapsed"の要素だけ。

このcollapsedをボタンを押したときに外したり、つけたりして大きさを変える。

ちなみに純粋なcssに変えるとこう書くことができる。

.content.collapsed { 
	max-height: 0;
}

js

jsでボタンを押したとき、クラスの付与などを行う。

const button = document.querySelector("#menu-btn")
button.addEventListener("click", toggleMenu)

この二行でボタン要素の取得と、clickした際にどんな関数を発火させるかを定義する。

ボタンを押したときの関数toggleMenuはこれ。

function toggleMenu() {
  const content = document.querySelector(".content")
  content.classList.toggle("collapsed")
}
const content = document.querySelector(".content")

で要素を取得してから、

content.classList.toggle("collapsed")

でクラスの付与/削除を行う。
toggleはその要素に対象クラスが付与されているときにクラスを削除し、クラスが存在しないときはクラスを付与する便利メソッドである。

これだけで完成。

アクセシビリティも対応したいならこれも記述する。

対象要素.setAttribute(
	"aria-expanded",
	writingStatusCard.getAttribute("aria-expanded") === "true" ? "false" : "true",
)

さっきのに合体するならこんな感じ

function toggleMenu() {
  const content = document.querySelector(".content")
  content.classList.toggle("collapsed")
  
  content.setAttribute(
		"aria-expanded",
		writingStatusCard.getAttribute("aria-expanded") === "true" ? "false" : "true",
	)
}

Note

AI補足

aria-expanded属性は、スクリーンリーダーなどの支援技術に対して、要素(今回はメニュー)が**開いているか(true)閉じているか(false)**の状態を伝えます。これにより、視覚に頼らずともメニューの状態を正確に把握できるようになります。」

発展例

ボタンとかはもっとわかりやすく格好良くできる。

<div class="wrapper">
	<button type="button" id="menu-btn" aria-expanded="false">
		<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="5 8 14 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="fold">
			<polyline points="6 9 12 15 18 9"></polyline>
		</svg>
	</button>
	
	<div class="menu">
		<div class="content">
			<h1>Hello World</h1>
		</div>
	</div>
</div>
.wrapper {
  border: 1px solid red;
}
 
.content {
  transition: 0.3s ease;
  max-height: 100px;
  overflow: hidden;
  
  &.collapsed {
    max-height: 0;
  }
}
 
#menu-btn {
  border: none;
  background: none;
  transition: 0.3s ease;
    
   &.collapsed {
    transform: rotate(-90deg);
  }
}
function toggleMenu() {
  const content = document.querySelector(".content")
  content.classList.toggle("collapsed")
  
  const btn = document.querySelector("#menu-btn")
  btn.classList.toggle("collapsed")
}
 
const button = document.querySelector("#menu-btn")
button.addEventListener("click", toggleMenu)
 

ボタンをsvgにして、そのアイコンも回転させるようにしている。
滑らかにアニメーションがされて、かなりUI的にも悪くなくなった。

おわり

一般にアコーディオンといわれるUI。
横方向にも作成することができます。
ただし、max-heightに固定値しか適用できないことに注意して使ってくださいね。