JavaScriptを用いて複数の投稿にプルダウンメニューを実装する方法

記事投稿系のポートフォリオを作成する中で、各記事に対してプルダウンメニューで編集・削除ボタンを作成しました。この時、複数の要素に対してイベント発火させるコードを書いたのが初めてだったので、1つの要素とイベント発火させる場合と比較しながら要点をまとめてみました。

# 使用したHTMLのコード

```HTML:index.html.erb
<div class="post-list">
    <% @articles.each do |article| %>
      <div class="first-show">
        <p class="post-destination">目的地：<%= @article.title %></p>
        <% if user_signed_in? && current_user.id == @article.user_id %>
          <div class="bar-btn", id="bar"> # クリック時にイベント発火
          ...
          <ul class="bar-contents", id="pull-down"> #普段は非表示、クリックした時にリストが表示される
            <li class="bar-list">
              <%= link_to '編集', edit_article_path(@article.id), class: "list-btn" %>
            </li>
            <li class="bar-list">
              <%= link_to '削除', article_path(@article.id), method: :delete, class: "list-btn" %>
            </li>
          </ul>
          </div>
        <% end %>
    <% end %>
```

# 比較するJavaScriptのコード
プルダウンメニューを実装する箇所が、1つの要素に対する場合と複数の要素に対する場合で比較を行います。それぞれのコードは下記の通りです。

## 1つの要素に対してプルダウンメニューを実装する場合

```Javascript:bar_btn.js
window.addEventListener("load", ()=>{
  const pullDownButton = document.querySelectorAll(".bar-btn");
  const indicateButton = document.querySelectorAll(".bar-contents");

  pullDownButton.addEventListener("click", ()=>{
    if (indicateButton.getAttribute("style") == "display: block;") {
      indicateButton.removeAttribute("style", "display: block;");
    } else {
      indicateButton.setAttribute("style", "display: block;");
    }
  });
});
```
## 複数の要素に対してプルダウンメニューを実装する場合

```Javascript:bar_btn.js
window.addEventListener("load", ()=>{
  const pullDownButton = document.querySelectorAll(".bar-btn");
  const indicateButton = document.querySelectorAll(".bar-contents");

  for ( let i = 0; i < pullDownButton.length; i++) {
    pullDownButton[i].addEventListener("click", () => {
      if (indicateButton[i].getAttribute("style") == "display: block;") {
        indicateButton[i].removeAttribute("style", "display: block;");
      } else {
        indicateButton[i].setAttribute("style", "display: block;");
      }
    });
  }
});
```
それぞれのコードについて、以下で詳しく見ていきます。

# ① HTMLの要素取得部分（1~3行目）
上の画像では、getElementByIdで要素を取得しています。1箇所だけイベント発火させたい場合にはこの表現でいいのですが、今回のように複数の要素にイベント発火させたい場合、これでは指定した要素の内、一番上にある要素しか取得されません。
一方、下の画像では、querySerectorAllで要素を取得しているため、指定した要素全てを取得できます。取得した要素は下記のように、配列の形で取得されます。
![console.log(pullDownButton)の出力.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/829213/6aa0f37b-6376-8d2e-a4b3-f88566c22da9.jpeg)


# ② プルダウンメニュー実装部分（5行目以降）
プルダウンメニュー実装部のul要素には、display: none;が指定してあるため、通常時、編集・削除ボタンは表示されません。これに対し、上記コードでは、プルダウンメニューボタンクリック時に、display: block;が記述され、編集・削除ボタンが表示されます。
これを、複数の要素に対してどう記述するかですが、①で述べた通り、querySerectorAllで取得した要素は配列で取得されます。そのため、pullDownButton.addEventListner(...)のように、直接イベント発火する記述ができません。これを解決するため、下の画像では、forを用いて、各i番目の要素に対して、イベント発火しています。
以上より、下記のように、複数の投稿に対してプルダウンメニューを実装することができました。
![2020-12-23 22.42のイメージ.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/829213/3267f1ea-3161-69d2-acae-97d90751dfa7.jpeg)

# 終わりに
これまで、getElementByIdで要素を取得するコードしか書いて来なかったので、複数の要素を取得するコードは、書いてみれば基本的なことばかりなのですが、中々苦戦しました。
中でも、querySerectorAllで要素を取得するということと、要素が配列で取得されるという点が重要だと感じました。