jQueryを使って選択した画像をプレビュー表示させる

# はじめに
画像投稿する際、投稿画像選択時に選択した画像（今回は１枚のみ）をプレビュー表示するまでを解説したいと思います。

# 完成イメージ
![imageupload.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683220/67a72f29-3075-3412-7417-4a978abae763.gif)



#環境
MacOS 10.15.7
ruby 2.6.5
Ruby on Rails 6.0.0

# 前提条件
jQueryの導入が完了済みであること。

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

## ①画像投稿フォームを作成
はじめに、画像の投稿フォームを作成していきます。
▼フォームの完成イメージ
![imageinput 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683220/738bed7c-77b2-bf14-d868-af9761246e83.png)


```erb:html.erb

<div class='image'>
  <label class="labelName" for="imageSpace">Photo </label>
  <input  class='imageInput' placeholder='写真を選択してください' type='file'>
</div>
<div class="prevImageBox">
</div>
```

```css:scss

.image {
  color: #ffffff;
  padding: 10px 16px 8px;
  margin-top: 8px;
  display: flex;
}

.imageInput {
  color: #000000;
  background-color: #fff;
  border-radius: 5px;
  border: 1px solid #ccc;
  margin-left: 10px;
}
```

上記の記述で出来上がりです。
（フォームの背景の設定については今回省略しております）

htmlの

```html
<div class="prevImageBox">
</div>
```
の部分については、選択した画像をプレビュー表示させるための要素（箱）になります。

## ②jQueryで実装していく
まずはjsファイルを作成します。ファイル名はなんでも良いですが今回は「image.js」というファイルを作成して、jQueryの記述をしていきます。

```javascript:image.js

$(function(){
  
  // <div class='image'>の直後の兄弟要素（今回の場合は<div class='prevImageBox'>がそれに該当）を取得。
  const prevImage = $('.image').next();

  // プレビュー表示部分のhtml
  function buildHTML(id,image) {
    let html = `<div class="prevImageBox">
                  <div class="upper-box">
                    <img src=${image} alt="preview">
                  </div>
                  <div class="lower-box">
                    <div class="delete-box">
                      <div class="delete-btn" data-delete-id= ${id}>削除</div>
                    </div>
                  </div>
                </div>`
    return html;
  }
  

  // ===========================================
  // imageInputの値が変更したとき(画像が選択された時）の処理
  // ===========================================
  $(document).on('change', '.imageInput', function() {
    //imageInputのidを取得
    let id = $(this).attr('id');
    //選択したfile（画像）のオブジェクトを取得
    let file = this.files[0];
    // PC内にあるファイルをアプリケーションに非同期で読み込む
    let reader = new FileReader();
    //readAsDataURLで指定したFileオブジェクトを読み込む
    reader.readAsDataURL(file);
    //読み込み時に発火するイベント
    reader.onload = function() {
      // 直前に実行されたイベント（imageファイルの読み込み）を変数imageに代入
      let image = this.result;
      //htmlを作成
      let html = buildHTML(id,image);
      //<div class='prevImageBox>にプレビュー画像を追加
      $(prevImage).append(html);
      //画像が選択された状態（プレビュー画像表示中）だと、画像選択フォームを隠す
      $('.imageInput').hide();
    }
  });


  // ==================================
  //   プレビュー画像を削除する場合の処理
  // ==================================
  $(document).on('click', '.delete-btn', function() {
    let id = $(this).attr('data-delete-id')
    //画像を消去
    $(this).parent().parent().parent().remove();
    //画像選択フォームの中身を空にする
    $('.imageInput').val("");
    //画像選択フォームを表示
    $('.imageInput').show();
  });
});
```

以上が、jQueryの記述となります。

プレビュー画像部分のCSSも載せておきます。

```scss:プレビュー画像部分のcss
.prevImageBox {
  display: block;
  width: 10vw;
  margin: 0 15px 10px 0;
  padding-left: 45px;
  .upper-box {
    height: 100%;
    width: 100%;
    img {
      width: 100%;
      height: 100%;
    }
  }
  .lower-box {
    display: flex;
    text-align: center;
    .delete-box {
      color: #00b0ff;
      width: 100%;
      height: 50px;
      line-height: 50px;
      border: 1px solid #eee;
      background: #f5f5f5;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: pointer;
      .delete-btn {
        background-color: #f5f5f5;
        line-height: 40px;
        width: 60px;
      }
    }
  }
}
```
プレビュー画像のサイズについては.prevImageBoxのwidthで調整してみてください。

# 簡単に解説
４行目で

```javascript
const prevImage = $('.image').next();
```
として、「nextメソッド」を使って、imageクラスの兄弟要素にあたるprevImageBoxを取得して、それを変数prevImageに格納しています。

nextメソッドについては以下が参考になります。
http://js.studio-kingdom.com/jquery/traversing/next

６〜１９行目の

```javascript
function buildHTML(id,image) {
    let html = `<div class="prevImageBox">
                  <div class="upper-box">
                    <img src=${image} alt="preview">
                  </div>
                  <div class="lower-box">
                    <div class="delete-box">
                      <div class="delete-btn" data-delete-id= ${id}>削除</div>
                    </div>
                  </div>
                </div>`
    return html;
  }
```
に関しては、実際に表示させるプレビュー画像部分のhtmlとなります。
構造としては、PrevImageBoxという大枠の中に、upper-boxとlower-boxの２段に分けた構造になります。
upper-boxにはイメージ画像を導入し、lower-boxには画像削除用ボタンを搭載する形となっております。


あとは、
画像を選択する時の処理

```javascript
// ===========================================
  // imageInputの値が変更したとき(画像が選択された時）の処理
  // ===========================================
  $(document).on('change', '.imageInput', function() {
    //imageInputのidを取得
    let id = $(this).attr('id');
    //選択したfile（画像）のオブジェクトを取得
    let file = this.files[0];
    // PC内にあるファイルをアプリケーションに非同期で読み込む
    let reader = new FileReader();
    //readAsDataURLで指定したFileオブジェクトを読み込む
    reader.readAsDataURL(file);
    //読み込み時に発火するイベント
    reader.onload = function() {
      // 直前に実行されたイベント（imageファイルの読み込み）を変数imageに代入
      let image = this.result;
      //htmlを作成
      let html = buildHTML(id,image);
      //<div class='prevImageBox>にプレビュー画像を追加
      $(prevImage).append(html);
      //画像が選択された状態（プレビュー画像表示中）だと、画像選択フォームを隠す
      $('.imageInput').hide();
    }
  });
```

プレビュー画像を削除する時の処理

```javascript
// ==================================
  //   プレビュー画像を削除する場合の処理
  // ==================================
  $(document).on('click', '.delete-btn', function() {
    let id = $(this).attr('data-delete-id')
    //画像を消去
    $(this).parent().parent().parent().remove();
    //画像選択フォームの中身を空にする
    $('.imageInput').val("");
    //画像選択フォームを表示
    $('.imageInput').show();
  });
```

を分けて記述しております。

# 最後までご覧いただきありがとうございました。