【Rails7】メニューバーのタブの切り替えをJavaScritp + Stimulus で実装してみた

どうも、ヒロキです。

今回はRails7でメニューバーのタブの切り替えをJavaScript、およびそのフレームワークのStimulusを使って実装してみました。

今後、同じ機能を再現するための覚書用と、後学のため記事にまとめておきます。

どなたかの参考になれば幸いです。

目次

開発環境

  • Ruby 3.1.2
  • Ruby on Rails 7.0.4
  • tailwindcss 3.1.8
  • M1 Macbook Air 2020
  • mac OS Monterey (ver. 12.4)
  • ターミナル bash (Rosetta 2 使用

出来上がりイメージ

以下のようなイメージで、メニュータブの切り替えを実装していきます。

タブの切り替えメニューを実装する流れ

ここでは以下を前提として話を進めていきますので、実装前に確認および準備しておいてください。

  • Stimulusコントローラー作成済み
  • CSSフレームワークはTailwindを使用(導入済み)

ちなみに、Stimulusコントローラーは$ rails g stimulus [コントローラー名]コマンドを実行すると、

app/javascript/controllers以下に[コントローラー名]_controller.jsという名称で自動生成されます。

それではいきます。

ナビゲーションメニューを作成

今回、ナビゲーションメニュー(navbar)のコードは公式ページのサンプル集から引用してアレンジしていくことにします。

app/views/layouts以下に_navigation.html.erbを新規作成し、サンプル集から引用したコードを貼り付けます。

コードは今回のデモ作成用に少しアレンジしています。

<div class="min-h-full">
  <nav class="bg-gray-800">
    <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
      <div class="flex h-16 items-center justify-between">
        <div class="flex items-center">
          <div class="flex-shrink-0">
            <img class="h-8 w-8" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" alt="Your Company">
          </div>
          <div class="hidden md:block">
            <div class="ml-10 flex items-baseline space-x-4">

        <!------------- 今回、アレンジした箇所↓↓ -------------->

              <a class="is-active" aria-current="page" data-navbar-target="menu" data-action="navbar#menuClick">ダッシュボード</a>

              <a class="not-active"  data-navbar-target="menu" data-action="navbar#menuClick">メニュー1</a>

              <a class="not-active"  data-navbar-target="menu" data-action="navbar#menuClick">メニュー2</a>

              <a class="not-active"  data-navbar-target="menu" data-action="navbar#menuClick">メニュー3</a>

             <!--------------------- ここまで ---------------------->

            </div>
          </div>
        </div>

     ・・・(以下省略)・・・

      </div>
    </div>
  </nav>
</div>

application.html.erbを編集

app/views/layouts/application.html.erbを以下のように編集します。

<!DOCTYPE html>
<html>
  <head>
  ・・・(省略)・・・
  </head>

  <body data-controller="navbar">
    <div class="min-h-screen bg-gray-100">
      <%= render 'layouts/navigation' %>
      <div class="container mx-auto px-8 py-3">
        <%= yield %>
      </div>
    </div>
  </body>
</html>

ここで、あらかじめ作成しておいたStimulusコントローラーnavbar_controller.jsがbody全体に適用されるようにするため、bodyタグにdata-controller="navbar"を追記しています。

コンテンツ表示用のビューを作成する

メニュータブの切り替えと連動して表示されるコンテンツ用のビューを作成します。

今回は簡単なデモということで、以下のようにシンプルなコードにしました。

<h1 class="text-3xl mb-3">トップページ</h1>

<div>
  <div data-navbar-target="content">
    <p class="mb-3">ダッシュボード</p>
    <%= image_tag "dashboard.png", class: "w-full" %>
  </div>

  <div class="hidden" data-navbar-target="content">
    <p class="mb-3">コンテンツ1</p>
    <%= image_tag "content01.png", class: "w-full" %>
  </div>

  <div class="hidden" data-navbar-target="content">
    <p class="mb-3">コンテンツ2</p>
    <%= image_tag "content02.png", class: "w-full" %>
  </div>

  <div class="hidden" data-navbar-target="content">
    <p class="mb-3">コンテンツ3</p>
    <%= image_tag "content03.png", class: "w-full" %>
  </div>
</div>

ちなみに、image_tagではapp/assets/images以下に保存しておいた画像を呼び出しています。

Stimulusコントローラーを編集

最後にStimulusコントローラーにJavaScriptコードを書いていきます。

import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="navbar"
export default class extends Controller {
  static targets = ["menu", "content"]

  /* メニュータブをクリックしたら発火するアクション */
  menuClick(event){
    const menus = this.menuTargets // メニューバー全体の要素
    const current = event.currentTarget // クリックしたメニューバーの要素(現在地)
    const currentIndex = menus.indexOf(current) // クリックしたメニューバーのインデックス番号
    const contents = this.contentTargets // コンテンツ全体の要素

    menus.forEach((menu, index)=>{ // 初期化
      if(current.classList.contains("not-active")){
        menu.classList.remove("is-active")
        menu.classList.add("not-active")
        contents[index].classList.add("hidden")
      }
    })

    if(current.classList.contains("not-active")){ // クリックしたメニューのclassをアクティブにする
      current.classList.remove("not-active")
      current.classList.add("is-active")
      contents[currentIndex].classList.remove("hidden") // クリックした要素のコンテンツを表示する
    }
  }

}

以上、これで出来上がりイメージのような動作になればOKです。

参考資料

BLOG AND DESTROY
【JavaScript・CSS】タブの切り替えメニューを実装する方法|複数箇所の場合も 【JavaScript・CSS】タブの切り替えメニューを実装する方法|複数箇所の場合も|今回はJavaScript・CSSを利用し、タブメニューを切り替えてコンテンツの内容を変更する例と...
Qiita
【Stimulus】Stimulusをクリックした要素を取得する方法 - Qiita はじめにRails7 でデフォルトでインストールされるようになった Stimulus でクリックした要素を取得する方法を忘備録として残しておきます。環境Rails 7.0.2Ruby 3.1…
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

愛知の34歳。無職で暇になり始めたプログラミング(Ruby on Rails)の忘備録をまとめたブログです。最近は別にやりたいことができたのでプログラミングほぼやっていません。気が向いたらまた再開するかも。僕の日常はメインブログの方で更新しています。

コメント

コメントする

目次