CSSでslideDownを実装する

CSSのgrid-template-rowsを使って、jQueryのslideDown()のようなアニメーションを実装する方法を見かけたので、試してみます。

サンプルコード

例として、アコーディオンの実装を試してみます。

<div class="acc">
  <button class="acc_btn">リゼ・ヘルエスタ</button>
  <div class="acc_contents">
    <div class="acc_contents-inner">
      <p>ヘルエスタ王国の第二皇女。<br>
        文武両道学園主席、真面目で誰にでも優しくかなりの人望がある。<br>
        王位継承の資格者として日々鍛錬や人とのコミュニケーションを大事にしている。</p>
      <p>ファンアート:<a href="https://twitter.com/hashtag/%E3%83%98%E3%83%AB%E7%B5%B5%E3%82%B9%E3%82%BF">#ヘル絵スタ</a></p>
    </div>
  </div>
</div>

アコーディオンのコンテンツ部分(.acc_contents)を入れ子(.acc_contents-inner)にしている点がポイントです。

.acc_contentsに対してgrid-template-rows で0frを設定して、開く際は1frになるようにします。

.acc_contents {
	display: grid;
	grid-template-rows: 0fr;
	transition: grid-template-rows 1s;
}
.acc_contents.is-open {
	grid-template-rows: 1fr;
}
.acc_contents-inner {
	overflow: hidden;
}

grid-template-rowsの0frから1frへの変更時は、transitionを使ったアニメーションの設定が可能になります。
ただそれだけだとコンテンツの中身が非表示にならないため、インナー要素(.acc_contents-inner)に対してoverflow: hidden を設定して非表示になるようにしています。

最後にJavaScriptで開閉時のclassの付け替え処理を行います。

document.querySelector('.acc_btn').addEventListener('click', function() {
  document.querySelector('.acc_contents').classList.toggle('is-open');
});

これでスライドで開閉するアコーディオンの実装ができました。
スライドで開閉するアコーディオンのデモページ

detailsタグとsummaryタグを使ったアコーディオンの場合も同じように試してみます。
まずはうまくいかなかった例です。

<details class="acc">
  <summary class="acc_btn">リゼ・ヘルエスタ</summary>
  <div class="acc_contents">
    <div class="acc_contents-inner">
      <p>ヘルエスタ王国の第二皇女。<br>
        文武両道学園主席、真面目で誰にでも優しくかなりの人望がある。<br>
        王位継承の資格者として日々鍛錬や人とのコミュニケーションを大事にしている。</p>
      <p>ファンアート:<a href="https://twitter.com/hashtag/%E3%83%98%E3%83%AB%E7%B5%B5%E3%82%B9%E3%82%BF">#ヘル絵スタ</a></p>
    </div>
  </div>
</details>

detailsタグの場合はopen属性が自動で付与されるため、open属性を使って開閉アニメーションの設定を行います。

.acc_contents {
	display: grid;
	grid-template-rows: 0fr;
	transition: grid-template-rows 1s;
}
.acc[open] .acc_contents {
  grid-template-rows: 1fr;
}
.acc_contents-inner {
	overflow: hidden;
}

detailsタグとでうまくいかなかった場合デモページ
detailsタグはopen属性が付与された時点で表示、削除された時点で非表示となるため、この実装方法だと閉じる際にアニメーション前に非表示になってしまいます。
また、開く際にも挙動が不安定で、スライドのアニメーションが発生する場合としない場合があるようでした。

開閉のアニメーションをopen属性ではなく、別途data属性を付与して管理するようにしてみます。

const details = document.querySelector('.acc');
const summary = document.querySelector('.acc_btn');
const contents = document.querySelector('.acc_contents');

summary.addEventListener('click', function(e) {
  // デフォルトの開閉処理を停止
  e.preventDefault();
  // アコーディオンが開いている場合
  if(details.open) {
    details.dataset.acc = 'close';
  // アコーディオンが閉じている場合
  } else {
    details.open = true;
    // open属性の付与と同じタイミングでのアニメーション開始だとうまくいかないことがあるので少しずらす
    details.dataset.acc = 'wait';
    setTimeout(function() {
      details.dataset.acc = 'open';
    }, 10);
  }
});

// アコーディオンの開閉アニメーション終了時
contents.addEventListener('transitionend', function(e) {
  // アコーディオンの閉じるアニメーション終了時
  if(details.dataset.acc === 'close') {
    details.open = false;
    details.dataset.acc = '';
  }
});

open属性のデフォルトの付け替えを停止した上で、任意のタイミングで付与・削除するようにしています。
スライドのアニメーションはdata-acc属性を使った管理で、ボタンクリック後に開くアニメーションが開始されるまではwait、開くアニメーション実行時はopen、閉じるアニメーション実行時はclose、閉じるアニメーション終了後は値なしになるようにしています。

アニメーションの設定をopen属性からdata-acc属性を使った形に変更します。

.acc_contents {
	display: grid;
	grid-template-rows: 0fr;
	transition: grid-template-rows 1s;
}
:where(.acc:not([data-acc="wait"]):not([data-acc="close"])[open]) .acc_contents {
	grid-template-rows: 1fr;
	transition: none;
}
.acc[data-acc="open"] .acc_contents {
	grid-template-rows: 1fr;
	transition: grid-template-rows 1s;
}
.acc_contents-inner {
	overflow: hidden;
}

.acc:not([data-acc=”wait”]):not([data-acc=”close”])[open] はJavaScriptがオフの場合の開閉用と、ページ内検索時の開閉用に設定しています。
これで意図したアコーディオンの挙動にすることができました。
detailsタグでうまくいった場合のデモページ

参考サイト

このエントリーをはてなブックマークに追加

関連記事

コメントを残す

メールアドレスが公開されることはありません。
* が付いている欄は必須項目です

CAPTCHA


コメントが承認されるまで時間がかかります。

2024年10月
 12345
6789101112
13141516171819
20212223242526
2728293031