2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【JavaScript】複数のアコーディオンパネルを作成する方法

Last updated at Posted at 2022-07-26

大阪でRailsを中心に学習している薬剤師エンジニア(初学者)こと、ヨマ(@yoma_2003)です!
JavaScriptで複数のアコーディオンパネルを作成する方法をまとめます。

※おことわり※
断定口調でまとめますが、初学者であるため間違い等あればご指摘頂けると嬉しいです。

はじめに

今回作成したい目標物は、以下の様な複数のアコーディオンパネル(クリックで開閉する要素)です。

ezgif-3-4f45f72301.gif

特徴

  • クリックするとクリックしたBoxが開く
  • 別のBoxをクリックすると開いていたBoxが閉じ、クリックしたBoxが開く(常に開くのは1つだけ)
  • Box以外をクリックすると開いていたBoxが閉じる

結論

HTML
<div class="main_contents">
    <div class="box">
      <h3>Box1</h3>
      <p>クリックで内容表示</p>
      <div class="box_content">
        内容を記入します。内容を記入します。内容を記入します。内容を記入します。内容を記入します。内容を記入します。
      </div>
    </div>
    <div class="box">
      <h3>Box2</h3>
      <p>クリックで内容表示</p>
      <div class="box_content">
        内容を記入します。内容を記入します。内容を記入します。内容を記入します。内容を記入します。内容を記入します。
      </div>
    </div>
    <div class="box">
      <h3>Box3</h3>
      <p>クリックで内容表示</p>
      <div class="box_content">
        内容を記入します。内容を記入します。内容を記入します。内容を記入します。内容を記入します。内容を記入します。
      </div>
    </div>
    <div class="box">
      <h3>Box4</h3>
      <p>クリックで内容表示</p>
      <div class="box_content">
        内容を記入します。内容を記入します。内容を記入します。内容を記入します。内容を記入します。内容を記入します。
      </div>
    </div>
  </div>
CSS
.main_contents {
  margin: 50px auto;
  width: 800px;
  display: flex;
  justify-content: space-between;
}

.box {
  height: 100px;
  width: 180px;
  margin: 0 10px;
  padding: 5px;
  text-align: center;
  overflow: hidden;
  border: 1px solid skyblue;
  border-radius: 3px;
  transition: .2s;
  cursor: pointer;
}

.box:hover {
  box-shadow: 0px 0px 7px #aaa;
}

.box_content {
  margin-top: 20px;
}

.open {   /* Boxが開いた場合に付与するクラス */
  height: 260px;
}
JavaScript
const accordionBox = () => {
  isOpen = false;
  window.addEventListener("click", function(e) {
    if (isOpen == true && (e.target.closest('.box') == null)) {   // いずれかのBoxが開いた状態で、Box以外をクリック
      const OpenedBox = document.querySelector(".open");
      OpenedBox.classList.remove("open");
      isOpen = false;
    }
  });
  
  const boxes = document.querySelectorAll(".box");
  boxes.forEach(function(box) {
    box.addEventListener("click", function(e) {
      if (this.classList.contains("open") == true) {   // 開いているBox自身をクリック
        return;
      } else if (isOpen == true) {   // 開いているBox以外のBoxをクリック
        const OpenedBox = document.querySelector(".open");
        OpenedBox.classList.remove("open");
        this.classList.add("open");
      } else {   // いずれのBoxも開いていない状態で、Boxをクリック
        this.classList.add("open");
      }
      isOpen = true;
    })
  });  
};

window.addEventListener("load", accordionBox);

解説

今回の目標物の動作のポイントは、クリックしたときの条件分岐です。

クリックしたときの状態は大きく分けて2つその上で定義する必要がある条件は以下の4つです。

条件分岐

  • Box以外をクリック
    • いずれかのBoxが開いている状態
  • Boxをクリック
    • 開いているBox自身をクリック
    • 開いているBox以外のBoxをクリック
    • いずれのBoxも開いていない状態

これらの条件は以下のように定義できます。

いずれかのBoxが開いている状態でBox以外をクリック

isOpen = false;   // Boxの状態を定義(デフォルトはfalse)
window.addEventListener("click", function(e) {
    if (isOpen == true && (e.target.closest('.box') == null)) {処理}
});

まずisOpen == trueとすることで「いずれかのBoxが開いている状態」を定義します。

その上で、windowのクリックイベントをeで取得し、e.targetとすることでクリックイベントが発生した要素を取得します。

このクリックされた要素に対し、.closest('.box')とすることで、この要素とその親に、'.box'というノードがあるか探索します。

== nullとすることで、クリックした要素にboxクラスがない場合、つまり「Box要素以外をクリックした状態」を定義することができます。

開いているBox自身をクリック

const boxes = document.querySelectorAll(".box");   // 全てのBoxを取得
boxes.forEach(function(box) {   //それぞれのBoxに対して処理
    box.addEventListener("click", function(e) {
      if (this.classList.contains("open") == true) {処理}
      ・・・

まず全てのBoxを取得しforEachを使うことでそれぞれのBoxに対して処理を定義します。

this(クリックされたBox要素)に対して.classList.contains("open")とすることで、この要素がopenクラスを含むかを判断します。

== trueとすることで「開いているBox自身をクリックした状態」を定義することができます。

開いているBox以外のBoxをクリック

・・・
else if (isOpen == true) {処理}
・・・

isOpen == trueなのでいずれかのBoxが開いている状態を定義でき、かつ先ほどの「開いているBox自身をクリックした状態」以外に当たるので、「開いているBox以外のBoxをクリックした状態」を定義できます。

いずれのBoxも開いていない状態で、Boxをクリック

・・・
else {処理
・・・

先ほどのisOpen == trueという条件以外にあたるので、「いずれのBoxも開いていない状態で、Boxをクリックした状態」が定義できます。

openクラスの付与とisOpenの状態変更でパネルの開閉を管理

Element.classList.add("open")

条件分岐した中で.classListメソッドを使用してopenクラスをつけ外ししてBox要素を開閉、isOpenという変数でBox開閉状態を変更し管理します。

おわりに

要素の開閉自体はもう少し簡単に定義できると思いますが、これらの条件分岐を定義することで、Box開閉だけでなく更に処理を追加することもできます。

参考

2
1
2

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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?