🔍

【Rails & JavaScript】スライドインする検索フォームの実装

2023/07/27に公開

スライドインする検索フォーム

以下のように虫眼鏡アイコンをクリックすると、検索窓が出現するようにする。
詳細の動きは以下の通り。

  1. 虫眼鏡アイコンをクリックすると、検索窓が出現。
  2. 検索窓が出現した状態で虫眼鏡をクリックすると検索が実施。
  3. 検索窓と虫眼鏡以外の箇所をクリックすると、検索窓が戻る。

JavaScriptの実装準備は以下に記載。
https://zenn.dev/ganmo3/articles/c01ce76c09ae8d

実装編

以下の通り実装していく。

HTML

html
<% if controller_name != 'sessions' %>
  <div class="search_box" id="search-box">
    <button class="btn btn-lg search_icon" id="search-icon" style="background-color: #FF9494; color: white;">
      <i class="fa-solid fa-magnifying-glass"></i>
    </button>
    <div class="search_form pe-3" id="search-form" style="width: 0; direction: rtl;">
      <% @q = Item.ransack %>
      <%= search_form_for @q, url: search_path, class: "row justify-content-end" do |f| %>
        <div class="col-md-8 p-0">
          <%= f.search_field :name_cont, class: "form-control", id: "search-input", placeholder: "商品を検索" %>
        </div>
      <% end %>
    </div>
  </div>
<% end %>

解説

以下の要素を入れる。

  1. id="search-box": 検索ボックス全体を囲むdiv要素。
  2. id="search-icon": 検索アイコンのbutton要素。
  3. id="search-form": 検索フォームを囲むdiv要素。
  4. id="search-input": 検索フォームのinput要素。

CSS

application.css
.search_form {
  overflow: hidden;
  transition: width 0.3s;
}

.search_icon:hover {
  cursor: pointer;
}

解説

  • overflow: hidden
    要素の内容が要素の枠を超えてはみ出る場合に、はみ出した部分を隠す。

-transition: width 0.3s
横幅の変化のアニメーション設定。0.3秒で変化。

  • cursor: pointer
    マウスポインタが要素の上にあるとき、カーソルを指の形に変える。

JavaScript

script.js
document.addEventListener('turbolinks:load', function() {
  const searchIcon = document.getElementById('search-icon');
  const searchForm = document.getElementById('search-form');
  const searchInput = document.getElementById('search-input');
  let searchVisible = false;

  searchIcon.addEventListener('click', function() {
    if (!searchVisible) {
      searchForm.style.width = "200px";
      searchVisible = true;
      setTimeout(() => searchInput.focus(), 300);
    } else {
      searchForm.querySelector('form').submit();
    }
  });

  document.addEventListener('click', function(event) {
    if (searchVisible && !searchIcon.contains(event.target) && !searchForm.contains(event.target)) {
      searchForm.style.width = "0";
      searchVisible = false;
    }
  });
});

解説

  1. document.addEventListener('turbolinks:load', function() { ... });
    ページ遷移してもJavaScriptが発火されるようにTurbolinksを使う。

  2. const searchIcon = document.getElementById('search-icon');
    searchIconは、idが"search-icon"という名前のHTML要素を取得。検索アイコンのこと。

  3. const searchForm = document.getElementById('search-form');
    searchFormは、idが"search-form"という名前のHTML要素を取得。検索フォーム全体のこと。

  4. const searchInput = document.getElementById('search-input');
    searchInputは、idが"search-input"という名前のHTML要素を取得。検索ボックス(テキスト入力欄)のこと。

  5. let searchVisible = false;
    searchVisibleは、検索フォームが表示されているかどうかを表す変数。初期値はfalse(検索フォームが非表示)に設定。

  6. searchIcon.addEventListener('click', function() { ... });
    検索アイコン(searchIcon)をクリックしたときに実行される処理を設定する。

  7. if (!searchVisible) { ... }
    もし検索フォームが表示されていない場合(searchVisiblefalseの場合)は、以下の処理を実行。

  8. searchForm.style.width = "200px";
    searchFormの横幅を200ピクセルに設定。検索フォームが画面内にスライドインするようになる。

  9. searchVisible = true;
    searchVisibletrueに設定して、検索フォームが表示されていることを記録。

  10. setTimeout(() => searchInput.focus(), 300);
    300ミリ秒(0.3秒)後に、検索ボックス(searchInput)にフォーカスを当てることで、ユーザーがすぐに検索を入力できるようにする。

  11. else { ... }
    それ以外の場合(検索フォームが表示されている場合)、以下の処理を実行。

  12. searchForm.querySelector('form').submit();
    検索フォーム内のフォームを送信。これにより、実際に検索が実行される。

  13. document.addEventListener('click', function(event) { ... });
    ドキュメント全体に対してクリックイベントを設定。

  14. if (searchVisible && !searchIcon.contains(event.target) && !searchForm.contains(event.target)) { ... }
    もし検索フォームが表示されている場合(searchVisibletrueの場合)かつ、クリックした場所が検索アイコンや検索フォームの外側だった場合は、以下の処理を実行。

  15. searchForm.style.width = "0";
    searchFormの横幅を0に設定して、検索フォームを画面外にスライドアウトさせる。

  16. searchVisible = false;
    searchVisiblefalseに設定して、検索フォームが非表示になっていることを記録。

このように記述することで検索アイコンをクリックするとスライドインして検索フォームが表示され、再度クリックすると検索が実行されるようにできる。


ポートフォリオの準備に入りました。
何をつくるか、4候補程出して悩みに悩んでいます。

Discussion