# CSS x JS: Membuat Collapsible dan Accordion

*Collapsible* dan *Accordion* adalah komponen web yang mampu menampilkan dan menyembunyikan isi konten ketika judul dari konten tersebut diklik. Komponen ini umum digunakan pada konten yang menyajikan informasi tentang pertanyaan yang paling sering ditanyakan atau *Frequently Asked Questions* (FAQ).

Artikel ini akan membahas pembuatan komponen tersebut dengan memanfaatkan elemen HTML `<details>` dan `<summary`>. Untuk *Collapsible* kita akan membuatnya menggunakan CSS saja, sementara JavaScript baru akan kita pakai ketika membuat *Accordion*.

Sebelum mulai, berikut saya lampirkan kode latihan jika kamu ingin sambil mempraktikkannya:

```html
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Membuat Collapsible dan Accordion | divClassId</title>
  <link rel="stylesheet" href="./style.css">
  <script defer src="./script.js"></script>
</head>
<body>
  <aside class="faq">
    <h3>Frequently Asked Questions</h3>
    <div class="faq__items">
      <details open>
        <summary>
          <strong>Apa itu HTML?</strong>
          <img src="./chevron.svg" alt="">
        </summary>
        <p>HTML adalah bahasa <em>markup</em> yang digunakan untuk menyusun struktur web.</p>
      </details>
      <details>
        <summary>
          <strong>Apa itu CSS?</strong>
          <img src="./chevron.svg" alt="">
        </summary>
        <p>CSS adalah bahasa <em>stylesheet</em> yang digunakan untuk mengatur tampilan atau visual web.</p>
      </details>
      <details>
        <summary>
          <strong>Apa itu JavaScript?</strong>
          <img src="./chevron.svg" alt="">
        </summary>
        <p>JavaScript adalah bahasa pemrograman yang digunakan pada web agar bisa tampil dinamis dan interaktif.</p>
      </details>
    </div>
  </aside>
</body>
</html>
```

```css
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

:root {
  --color-black: #111111;
  --color-green: #0b6758;
  --color-white: #fcfcfc;
}

html {
  height: 100%;
}

body {
  background-color: var(--color-green);
  color: var(--color-black);
  font-family: sans-serif;
  height: 100%;
  line-height: 1.5;
}

.faq {
  padding: 20px 10px;
}

.faq > h3 {
  color: var(--color-white);
  font-size: 1.25rem;
  text-align: center;
}

.faq__items {
  
}

.faq__items > details {
  
}

.faq__items > details > summary {
  
}

.faq__items > details[open] > summary > img {
  
}

.faq__items > details > p {
  
}

```

Untuk gambar **chevron.svg** bisa kamu unduh pada tautan berikut:

[https://github.com/mochamadboval/divclassid/blob/collapsible-accordion/collapsible-accordion/chevron.svg](https://github.com/mochamadboval/divclassid/blob/collapsible-accordion/collapsible-accordion/chevron.svg)

Sekarang, mari kita buat!

## Membuat Collapsible

Jika kamu menjalankan latihan di atas, kamu akan melihat bahwa penggunaan elemen `<details>` dan `<summary>` akan langsung menghasilkan komponen ini. Jadi, tugas kita selanjutnya adalah hanya tinggal mengatur tampilannya saja.

![Tampilan elemen <details> dan <summary> yang langsung menghasilkan Collapsible](https://cdn.hashnode.com/res/hashnode/image/upload/v1658819965891/V8RvrIboFC.png align="center")

Untuk permulaan, kita beri *style* berikut terlebih dahulu agar elemen *container* FAQ memiliki *background* putih dan tampil di tengah (seperti judulnya):

```css
.faq__items {
  background-color: var(--color-white);
  margin: 10px auto 0;
  max-width: 360px;
  padding: 10px;
}

.faq__items > details {
  margin-bottom: 1px;
}
```

- `max-width: 360px` ditambahkan agar lebar *container* responsif dengan lebar maksimalnya sebesar 360px saja.
- `margin: 10px auto 0` ditambahkan untuk memberi jarak antara *container* dengan judul di atasnya sebesar 10px dan nilai `auto` akan membuatnya berada di tengah secara horizontal.
- `padding: 10px` ditambahkan agar konten di dalam *container* dengan tepinya memiliki jarak sebesar 10px.
- `margin-bottom: 1px` pada *selector* `.faq__items > details` ditambahkan agar ada sedikit jarak antara *item* atau masing-masing FAQ (efeknya akan terlihat nanti).

![Style awal dari Collapsible](https://cdn.hashnode.com/res/hashnode/image/upload/v1658819995494/2o7i_IFcm.png align="center")

Selanjutnya, kita beri *style* berikut untuk mengatur tampilan dari setiap judul *(item)* FAQ:

```css
.faq__items > details > summary {
  align-items: center;
  background-color: var(--color-green);
  color: var(--color-white);
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  list-style: none;
  padding: 10px;
}
```

- `list-style: none` ditambahkan untuk menghilangkan tanda panah bawaan elemen `<summary>`.
- `cursor: pointer` ditambahkan agar kursor *mouse* berubah ketika berada di atas judul *item* menjadi ikon yang menandakan bahwa itu bisa diklik.
- `display: flex` dan `align-items: center` ditambahkan untuk mengaktifkan CSS Flexbox pada elemen ini sehingga kita bisa mengatur posisi elemen-elemen di dalamnya agar ke tengah secara vertikal.
- `justify-content: space-between` ditambahkan agar jarak antara judul dan tanda *chevron* saling berjauhan.

![Tampilan judul item FAQ setelah diatur](https://cdn.hashnode.com/res/hashnode/image/upload/v1658820070175/IVXba-vMp.png align="center")

Saat ini tampilan dari setiap judul *item* sudah lebih baik. Namun, masih ada satu hal lagi yang bisa kita percantik, yaitu tanda *chevron* yang akan menyesuaikan arahnya tergantung apakah *item* FAQ sedang terbuka atau tidak.

Untuk persoalan ini CSS masih dapat menanganinya dengan memanfaatkan *selector* `details[open]` dan mengubah arah *chevron*-nya menggunakan fungsi CSS `rotateX()` milik *property* `transform`, seperti berikut:

```css
.faq__items > details[open] > summary > img {
  transform: rotateX(180deg);
}
```

![Kini arah tanda chevron menyesuaikan dengan terbuka tidaknya item FAQ](https://cdn.hashnode.com/res/hashnode/image/upload/v1658820126569/9NB0ObLqS.png align="center")

Terakhir, kita hanya perlu mengatur jarak paragraf di dalamnya dengan *style* berikut:

```css
.faq__items > details > p {
  padding: 10px;
}
```

![Tampilan utuh dari Collapsible](https://cdn.hashnode.com/res/hashnode/image/upload/v1658820203701/0fKayaKsF.png align="center")

## Membuat Accordion

*Accordion* sebenarnya serupa dengan *Collapsible*. Bedanya, komponen ini hanya dapat menampilkan satu *item* FAQ saja dan akan menyembunyikan *item* lainnya yang sedang terbuka.

Karena serupa, sekarang kita hanya tinggal membuat efek tersebut saja dengan bantuan JavaScript. Berikut adalah kodenya:

```js
document.addEventListener('DOMContentLoaded', () => {
  const faq = Array.from(document.querySelectorAll('.faq__items > details'));

  faq.forEach((item) => {
    item.addEventListener('click', (event) => {
      const active = faq.find((item) => item.open);

      if (!event.currentTarget.open && active) {
        active.open = false;
      }
    });
  });
});

```

Berikut penjelasannya:

- ```js
  document.addEventListener('DOMContentLoaded', () => {
    ...
  });
  ```
  *Event* `DOMContentLoaded` ditambahkan untuk memastikan kode JavaScript di dalamnya baru akan tereksekusi setelah kode HTML selesai dimuat dan tersedia pada DOM.

- ```js
  const faq = Array.from(document.querySelectorAll('.faq__items > details'));
  ```
  Kode ini akan mengambil semua elemen `<details>` dan menyimpannya ke dalam variabel `faq`.
  
  Perlu diperhatikan bahwa terjadi konversi tipe data dari yang semula berbentuk *NodeList object* menjadi *Array*. Ini penting karena nantinya kita akan menggunakan *method* `find()` yang hanya tersedia pada *Array*.

- ```js
  faq.forEach((item) => {
    item.addEventListener('click', (event) => {
      ...
    });
  });
  ```
  Kode ini akan melakukan perulangan pada variabel `faq` sehingga kita dapat menempatkan *event* `click` untuk setiap *item* (elemen) yang ditampungnya.

- ```js
  const active = faq.find((item) => item.open);
  ```
  Kode ini akan mencari elemen `<details>` yang sedang terbuka (memiliki atribut `open`) dan menyimpannya ke dalam variabel `active`.

- ```js
  if (!event.currentTarget.open && active) {
    active.open = false;
  }
  ```
  Kode ini akan mencocokkan variabel `active` yang bernilai `true` dengan nilai dari `!event.currentTarget.open`. Perlu diperhatikan bahwa tanda `!` di awal kode `event.currentTarget.open` akan membalik nilainya dari yang semula `false` menjadi `true`.
  
  Dengan begitu, variabel `active` yang menampung elemen `<details>` dengan atribut `open` dapat kita ubah nilainya menjadi `false`, sehingga *item* FAQ lainnya yang sedang terbuka akan otomatis tertutup dan tinggal menyisakan *item* yang baru saja kita klik yang terbuka.

![Tampilan setelah penerapan efek Accordion pada Collapsible sehingga hanya dapat membuka satu item FAQ saja](https://cdn.hashnode.com/res/hashnode/image/upload/v1658820256232/XJVm-OfOD.png align="center")

  ---

  Demikian pembahasan tentang membuat *Collapsible* dan *Accordion*. Semoga bermanfaat.

