jQueryを使って複数の投稿画像をマウスオーバーで切り替え表示する。

# はじめに
jQueryを使って、投稿した複数枚画像をマウスオーバーして表示を切り替えていく実装をやりたいと思います。
（投稿するのは自分自身の実装を記録として残すための意味でもあります）

# 完成イメージ
![changeShowImage.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683220/acc51145-b369-a5ec-cfe5-0ef87ccb4ad5.gif)

# 環境
MacOS 10.15.7
ruby 2.6.5
Ruby on Rails 6.0.0

# 前提条件
- jQueryが導入済みであること
- 画像の複数枚投稿が機能として実装されていること。
（複数枚投稿機能については後日UP予定です。）
- carrierwaveとmini_magickを導入済み。
- font-awosome-sass導入済み。

# 投稿者の状況
#### テーブル構造は次の通りです。
![Untitled Diagram-ページ2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683220/8a71bdeb-6726-42e0-f45d-966578bdeb23.png)

投稿サイトとなるので、利用者を「User」、投稿を「Post」、画像を「image」、としてテーブルを作成しております。
（テーブルの中身は投稿用に簡略化してます）

#### モデルは次の通りです。

```ruby:userモデル
has_many :posts, dependent: :destory
```

```ruby:postモデル
belongs_to :user

has_many :images, dependent: :destory

accepts_nested_attributes_for :images, allow_destroy: true
```

```ruby:imageモデル
belongs_to :post

mount_uploader :image, ImageUploader
```
以上です。
細かい部分は省略しますが、投稿（post)は画像（image）を複数保持できるのでhas_manyの関係性になります。
また、利用者（user）は複数投稿（post）を保有できるのでhas_manyの関係性になります。

#### コントローラー
コントローラーについてはpost_controller.rbを作成して、その中で投稿の閲覧、作成を制御してます。今回はpostコントローラーのshowの部分だけピックアップして記述します。

```ruby:post_controller.rb
def show
  @post = Post.find(params[:id])
end
```
こちらも投稿用に簡略化してます。


# 作業していきましょう！

作業の流れとしては、
①投稿画像を表示できるようにhtml.erbに記述する。
②each_with_indexメソッドを使って、投稿画像１つ１つにindex番号を割り振る。
③jsファイルにマウスオーバーして時に画像が切り替わる処理を記述
以上となります。


### ①投稿画像を表示できるようにhtml.erbに記述する。
今回は、showページに投稿画像を表示させたいので、show.html.erbに記述します。

```erb:show.html.erb
<%# 投稿トップ画像 %>
<div class='imagesContainer'>
  <%# 投稿画像（メイン画像）　%>
  <div class='showMainImage', id='mainImage', style="background-image: url(<%= @post.images[0].image%>);">
    <%# 都道府県名とアイコン %>
    <div class="areaInfomation">
      <div class="mapIcon">
        <i class="fas fa-map-marker-alt"></i>
      </div>
      <div class="areaName">
        東京都
      </div>
    </div>
    <%# bookmarkのアイコンとカウント %>
    <div class="postBookmark">
      <i class="fas fa-bookmark"></i>
      <div class="bookmarkCount">
        1
      </div>
    </div>
  </div>
  <%# 投稿画像一覧（最大５枚） %>
  <ul class="subImagesContainer">
    <% @post.images.each_with_index do |image, i| %>
      <div class='subImage', style="background-image: url(<%= @post.images[i].image%>);">
      </div>
    <% end %>
  </ul>
</div>
```
ちなみに

```erb
<div class="areaName">
  東京都
</div>
```
と

```erb
<div class="bookmarkCount">
  1
</div>
```

は今回、設定していないので、ハードコーディングでそれぞれ「東京都」と「１」を直接記述してます。
<br>
次にCSSになります。

```scss:show.scss
.imagesContainer {
  width: 50vw;
  padding: 50px 50px;
  display: flex;
  flex-direction: column;
}

// 投稿トップ画像===========================================================
.showMainImage {
  border: 1px solid #000000;
  margin-top: 10px;
  height: 40vh;
  border-radius: 20px;
  background-size: cover;
  background-position: center center;
  position: relative;
}

// 都道府県名とアイコン=======================================================
.areaInfomation {
  background-color: rgba($color: #000000, $alpha: 0.4);
  border: #ffffff;
  border-radius: 20px;
  color: #ffffff;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 4vh;
  width: 90px;
  margin: 5px 0 0 5px;
  position: absolute;
  top: 5px;
  left: 10px;
  .showUserIcon {
    font-size: 15px;
    color: #ffffff;
  }
  .areaName {
    font-size: 15px;
    margin-left: 6px;
  }
}

// 画像のbookmarkのアイコンとカウント===========================================
.postBookmark {
  position: absolute;
  top: 5px;
  right: 5px;
  background-color: rgba($color: #000000, $alpha: 0.4);
  border: #ffffff;
  border-radius: 20px;
  color: #ffffff;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 4vh;
  width: 4vw;
  margin: 5px 0 0 5px;
}

.bookmarkCount {
  margin-left: 0.5vw;
}


// 投稿画像一覧（横並びで最大５枚まで表示）=======================================
.subImagesContainer {
  display: flex;
  height: 10vh;
  .subImage {
    width: 10vw;
    height: 10vh;
    background-size: cover;
    background-position: center center;
  }
}
```

最終的に以下のようになると思います。（画像の内容は各人で異なります。）
![imagess.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683220/cdecef63-2dd4-cfeb-a6f4-c02213d11cb8.png)


以上となります。

### ②each_with_indexメソッドを使って、投稿画像１つ１つにindex番号を割り振る。（確認のみ）

show.html.erbの２４行目、２５行目に注目してください。

```erb

<% @post.images.each_with_index do |image, i| %>
  <div class='subImage', style="background-image: url(<%= @post.images[i].image%>);">
  </div>
<% end %>
```
今回、投稿画像１つ１つに対して、番号を割り振り、その番号で管理するようにします。
番号を割り振るにあたり、

`each_with_indexメソッド`を使って、それぞれの画像に番号を割り振る処理をしています。
具体的には、５枚の画像を投稿した場合、１枚目の画像に「０」、２枚目に「１」、３枚目に「２」、４枚目に「３」、５枚目に「４」の番号が割り振られ、表示されることになります。

ちなみに

```erb
<% @post.images.each_with_index do |image, i| %>
```
の

```erb
|image, i|
```
については、imageが「画像」、iが「インデックス番号」という意味です。
投稿画像数に応じて、１つ１つのデータに画像とインデックス番号をセットで渡すイメージです。

each_with_indexについてはrubyリファレンスを参照してみてください。
リファレンス：　https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/each_with_index.html

以上でeach_with_indexの説明は終了です。
<br>

### ③jsファイルにマウスオーバーして時に画像が切り替わる処理を記述
最後に、jsファイルにjQueryで投稿画像一覧でマウスオーバーした画像をトップ画像に反映させます。

今回は、「image.js」というファイルを新規作成して、記述をしたいと思います。(ファイルの名前はなんでも大丈夫です）

```javascript:image.js

$(function() {
  // class='subImage'にマウスを乗せるとイベントが発火
  $('.subImage').hover(function() {
    // マウスオーバーした画像のstyle(この場合、"background-image: url(<%= @post.images[i].image%>);")の値を取得し、それを変数Styleに代入）
    let Style = $(this).attr("style");
    // id='mainImage'側でstyleを先ほど定義した変数Styleの値で取得し直す。
    $("#mainImage").attr({style:Style});
    // mainImage側で表示している画像を一旦空っぽにする。
    $("#mainImage").val("");
    // 新たに取得したstyleの値で、画像を表示させる。
    $("#mainImage").fadeIn();
  });
});
```
以上となります。

まず、２行目で、subImageクラスにマウスオーバーした際に発火するイベント処理を記述しています。今回「hover」を使っていますが、クリックで画像を切り替えたい場合は「click」でもOKです。

3行目で

```javascript
let Style = $(this).attr("style");
```
とあるように、マウスを乗せた画像のstyle属性をattrメソッドによって取得します。attrメソッドは引数に指定した属性の値を取得することができるメソッドです。
（attrメソッドについての参照）https://techacademy.jp/magazine/26775

例えば、２枚目の画像を選択するとします。
each_with_indexメソッドでインデックス番号「１」を渡しているので、２枚目の画像は
`<div class='subImage', style="background-image: url(<%= @post.images[1].image%>);">`
となります。

したがって、$(this).attr("style");で取得するstyleの値は、
`"background-image: url(<%= @post.images[1].image%>);"`
となります。
最後に取得した値を「let Style」として変数で定義します。
<br>

次に4行目で、

```javascript
$("#mainImage").attr({style:Style});
```
として、idがmainImageとなっている部分（トップ画面のこと）で、先ほど定義した変数Styleの値をstyle属性で再設定しています。

先ほど挙げた例だと、Styleの値は、`"background-image: url(<%= @post.images[1].image%>);"`となります。

したがってid=mainImage側は最終的に、
`<div class='showMainImage', id='mainImage', style="background-image: url(<%= @post.images[１].image%>);">`
となります。

<br>
５行目では

```javascript
$("#mainImage").val("");
```
として、現在トップ画像で表示している画像を一旦空っぽにしています。
val()メソッドは、HTMLタグ内に記述しているvalue属性を所得・変更できるものです。valの引数を（""）とすることで空にすることができます。
（valメソッドについての参照）https://www.sejuku.net/blog/45297


<br>
最後に、

```javascript
$("#mainImage").fadeIn();
```
で新たに取得したstyleの値を基に、画像を表示させています。

以上でマウスオーバーで画像を記述することができるようになります。

# 最後に
初学者のため、認識が間違っている部分があるかと思います。間違っている箇所がある、この認識は正しくない、などのご意見がありましたらご指摘いただけますと幸いです。また、もしよろしければLGTMいただけますと嬉しいです。
以上、最後までご覧いただきありがとうございました！！