13
21

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 3 years have passed since last update.

【Rails、JavaScript】モーダルウィンドウに写真詳細画面を表示させる

Posted at

概要

一覧の写真をクリックすると、モーダルウィンドウ内に写真詳細画面が表示される機能を実装します。

参照

モーダルウィンドウ

基本的なモーダルウィンドウの作成方法とアニメーションの付け方は、これらの記事を参考にしました。

Ajax

remote: trueによるAjaxの実装方法は、この記事がわかりやすいです。

完成イメージ

indexビューの写真をクリックするとshowビューの写真詳細画面がモーダル内に表示される。

Image from Gyazo

開発環境

macOS Big Sur バージョン 11.2
ruby 2.6.5
Rails 6.0.3.4

実装の流れ

  1. 仮のモーダルウィンドウを作成する。
  2. 写真クリックでshowアクションが実行され、js.erbファイルが呼び出されるようにする。
  3. JavaScriptにより、モーダルウィンドウ内にshowビューが格納され表示されるようにする。

実装

1. 仮のモーダルウィンドウを作成する。

indexとshowアクションはphotosコントローラーで定義されています。

photos_controller.rb
class PhotosController < ApplicationController
  省略
  def index
    省略
  end

  def show
    省略
  end
  省略
end

index.html.erbの適当な位置に仮のモーダルウィンドウを設置します。
hiddenクラスで最初は表示されないようにします。

index.html.erb
# モーダルの周囲を薄暗くするマスク
  <div id="mask" class="hidden"></div>
# モーダル
# あとからJavaScriptで<div>内のコードをshowビューのコードに置き換えます。
  <div id="modal" class="hidden">
    <p>モーダルウィンドウ<p>
  </div>

cssはこのようになります。詳細は参照動画を御覧ください。

index.css
/* hiddenクラスがないとマスクとモーダルが表示される(JavaScriptでhiddenクラスを消したとき)*/
#mask {
  background-color: rgba(0, 0, 0, 0.5);
  position: fixed;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
  z-index: 1;
}

#modal {
  position: fixed;   /* スクロールで移動しないようmodalの位置を固定します */
  top: 10vh;
  /* 以下3行:モーダルを左右中央揃えにする方法 */
  left: 0;
  right: 0;
  margin: 0 auto;
  transform: translate(0, 0) scale(1);
  transition: 0.2s;   /* transitionでアニメーションを作成 */
  z-index: 2;   /* マスクより上に重なるようにする */
  width: 935px;   /* showビューの幅に合わせて設定 */
}

/* hiddenクラスで最初は表示されないようにする */
#mask.hidden {
  opacity: 0;
  visibility: hidden;
}

#modal.hidden {
  opacity: 0;
  visibility: hidden;
  transform: translate(0, 0) scale(0.8);   /* 大きさ0.8倍から等倍にモーダルが拡大される */
}

2. 写真クリックでshowアクションが実行され、js.erbファイルが呼び出されるようにする。

一覧の写真は部分テンプレートにしてあります。
link_toメソッドでshowアクションが実行され、remote: trueを設定することにより、リクエストをjs形式にします。

index.html.erb
# 部分テンプレートを呼び出す
<%= render partial:"shared/photos", locals: {photos: @photos} %>
_photos.html.erb
<% photos.each do |photo| %>
# 写真をクリックしたときの実行アクション(showなのでphoto_path(photo.id))とリクエスト形式(jsなのでremote: true) を指定
  <%= link_to photo_path(photo.id), remote: true do %>
    <%= image_tag photo.image.variant(省略), class: "photo-img", id: "photo-img" %>
 <% end %>
<% end %>
photos_controller.rb
class PhotosController < ApplicationController
  省略

  def show
    省略
    respond_to do |format|
      format.html
      # link_toメソッドをremote: trueに設定したのでリクエストはjs形式で行われる(詳しくは参照記事をご覧ください)
      format.js
    end
  end
  省略
end

3. JavaScriptにより、モーダルウィンドウ内にshowビューが格納され表示されるようにする。

showビューを取得するために、show.html.erbを部分テンプレート化します

_show.html.erb
(省略)(中身はshow.html.erbと同じ)

上記showアクションが実行されると、photosフォルダ内のshow.js.erbファイルが呼び出されます。ない場合は作成します。

show.js.erb
# モーダルを取得
var modal = document.getElementById('modal');
# マスクを取得
var mask = document.getElementById('mask');

# モーダルの中身をshowビューに置き換えます。
# 部分テンプレートのパス、localsの値はアプリにより異なります。
modal.innerHTML = '<%= escape_javascript(render partial: 'shared/photo_show', locals: { photo: @photo, comments: @comments, user: @user, comment: @comment }) %>';

# hiddenクラスを消してモーダルとマスクを表示させます。
modal.classList.remove('hidden');
mask.classList.remove('hidden');

# モーダルの外側(マスク)をクリックするとhiddenクラスが書き込まれ、モーダルとマスクが再び非表示となります。
mask.addEventListener('click', () => {
  modal.classList.add('hidden');
  mask.classList.add('hidden');
  });

おわりに

注意点として、モーダル内で"いいね"や"コメント投稿"などを実行する場合は、いずれもAjax化しておく必要があります。Ajax化されていない場合、それらを実行すると他のページにリダイレクトされたりエラーとなったりします。

↓Ajax化されていると、このようにモーダル内で"いいね"や"コメント投稿"ができます。

Image from Gyazo

以上、参考になりましたら幸いです。
間違いなどありましたら、ご指摘いただきたいです。

13
21
0

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
13
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?