画像のプレビュー機能実装

#はじめに
Railsでオリジナルアプリを制作しています。このアプリに画像投稿機能を実装しました。画像をアップロードした後(データベースに保存する前)の状態でプレビューを表示しましたので、書き記します。
※コードひとつひとつに解説を挟むので、最後に完成コードを載せています。

#目次
1.事前準備
2.プレビュー機能の実装

#1.事前準備
JavaScriptを使用しますので、jsファイルを準備します。ページが読み込まれたときに動作するように定義します。


```js:app/javascript/packs/preview.js
document.addEventListener('DOMContentLoaded', function(){
  const ImageList = document.getElementById('image-list');
});
```

なお、getElementByIdで取得するimage-listは、html上のdivタグのid名です。このdivタグにプレビューを表示します。そのため、htmlに追記します(今回は送信ボタンの下に配置しました)。

```Ruby:○○.html.erb
<%= f.file_field :image %>
<%= f.submit '送信' %>
<div id="image-list"></div>
```
また、作成したjsファイルが読み込まれるようにapplication.jsも編集します。コメントアウトしたturbolinksは画面遷移を高速化するGemです。ページ内の情報を全て読み込まず、HTMLだけを読み取るそうです。だからJavaScriptで設定したイベントが発火しない場合があります。

```JavaScript:app/javascript/packs/application.js
// require("turbolinks").start()  // コメントアウトする
require("@rails/activestorage").start()
require("channels")
require('./preview')  // 追記する
```

#2.プレビュー機能の実装
全部で3ステップ。
**2-1.画像情報の取得
はじめに画像情報を取得し、URLを生成します。画像情報の取得はinput要素の値に変更があった場合に行います。

```js:app/javascript/packs/preview.js
document.addEventListener('DOMContentLoaded', function(){
  const ImageList = document.getElementById('image-list');
 
  document.getElementById('desk_image').addEventListener('change', function(e){
  //↑desk_imageは画像選択ボタンのid//
    const file = e.target.files[0];
    const blob = window.URL.createObjectURL(file);
  });
});
```
選択した画像は、発火したイベントe、targetの中のfilesという配列に格納されています。それをfile変数に定義し、window.URL.createObjectURLでURLを生成します。

**2-2.取得画像の表示**
createElementで取得画像を入れ込むdiv要素とimg要素生成します。その後生成した要素をブラウザに表示します。

```js:app/javascript/packs/preview.js
document.addEventListener('DOMContentLoaded', function(){
  const ImageList = document.getElementById('image-list');
 
  document.getElementById('desk_image').addEventListener('change', function(e){
    const file = e.target.files[0];
    const blob = window.URL.createObjectURL(file);

  // 画像を表示するためのdiv要素を生成
    const imageElement = document.createElement('div');

  // 表示する画像要素を生成し、画像を入れ込む
    const blobImage = document.createElement('img');
    blobImage.setAttribute('src', blob);

  // 生成したHTMLの要素をブラウザに表示させる
    imageElement.appendChild(blobImage);
    ImageList.appendChild(imageElement);
  });
});
```
appendChildで指定した親要素の中に要素を追加する(使い方：親要素.appendChild(子要素))。

**2-3.エラー回避と編集可能にする**
2-2までで画像は表示されるようになりました。しかし、画像投稿ページ以外ではコンソール上でエラーが表示されます。これを回避するため条件分を追加します。URLにnew及びeditがある場合(画像の新規投稿、編集時)に適用するように記述します。

```js:app/javascript/packs/preview.js
if (document.URL.match( /new/ ) || document.URL.match( /edit/ )) {
  document.addEventListener('DOMContentLoaded', function(){
    const ImageList = document.getElementById('image-list');
 
 ーーーーーーーーーーーーーーー中略ーーーーーーーーーーーーーーーーーー 
    // 生成したHTMLの要素をブラウザに表示させる
      imageElement.appendChild(blobImage);
      ImageList.appendChild(imageElement);
    });
  });
}
```
画像を編集するときは、既存のプレビューを削除した後に新しい画像のプレビューを表示するようにする。

```js:app/javascript/packs/preview.js
if (document.URL.match( /new/ ) || document.URL.match( /edit/ )) {
  document.addEventListener('DOMContentLoaded', function(){
    const ImageList = document.getElementById('image-list');

    document.getElementById('message_image').addEventListener('change', function(e){
      // 画像が表示されている場合のみ、すでに存在している画像を削除する
      const imageContent = document.querySelector('img');
      if (imageContent){
        imageContent.remove();
      }
      const file = e.target.files[0];
      const blob = window.URL.createObjectURL(file);
      const imageElement = document.createElement('div');
      const blobImage = document.createElement('img');
      blobImage.setAttribute('src', blob);
      imageElement.appendChild(blobImage);
      ImageList.appendChild(imageElement);
    });
  });
}
```
以上