前提
ミニアプリを作っておりまして、現在は画像投稿機能の部分において、複数画像を投稿できるような機能を作っているところです。
実現したいこと
- プレビュー表示において、JavascriptにおけるNumberオブジェクトでプレビュー画像に番号をつけたい。
発生している問題
新しく選択した画像に番号は付けられていますが、全ての画像が同じ番号になっている状態です。
preview.js
1// file_fieldを生成・表示する関数 2 const buildNewFileField = () => { 3 // 2枚目用の画像選択ボタンを作成 4 const newImageFile = document.createElement('p'); 5 newImageFile.innerHTML = "画像を選択する"; 6 // 2枚目用のfile_fieldを作成 7 const newFileField = document.createElement('input'); 8 newFileField.setAttribute('class', 'hidden'); 9 newFileField.setAttribute('type', 'file'); 10 newFileField.setAttribute('name', 'learn[images][]'); 11 12 // 最後のfile_fieldを取得 13 const lastFileField = document.querySelector('input[class="hidden"][type="file"][name="learn[images][]"]:last-child'); 14 // nextDataIndex = 最後のfile_fieldのdata-index + 1 15 const nextDataIndex = Number(lastFileField.getAttribute('data-index')) +1; 16 newImageFile.setAttribute('data-index', nextDataIndex); 17 newFileField.setAttribute('data-index', nextDataIndex); 18 19 // 追加されたfile_fieldにchangeイベントをセット 20 newImageFile.addEventListener("change", changedFileField); 21 newFileField.addEventListener("change", changedFileField); 22 23 // 生成したfile_fieldを表示 24 const fileFieldArea = document.querySelector('.click-upload'); 25 fileFieldArea.appendChild(newImageFile); 26 fileFieldArea.appendChild(newFileField); 27 };
「最後のfile_fieldを取得」で画面に表示されている画像選択ボタンの情報を取得し、その後で新しい画像と画像選択ボタンに新しい番号をつけるように記述しているつもりでした。
preview.js
1// プレビュー画面を生成・表示する関数 2 const buildPreviewImage = (dataIndex, blob) =>{ 3 // 画像を表示するためのdiv要素を生成 4 const previewWrapper = document.createElement('div'); 5 previewWrapper.setAttribute('class', 'preview'); 6 previewWrapper.setAttribute('data-index', dataIndex); 7 8 // 表示する画像を生成 9 const previewImage = document.createElement('img'); 10 previewImage.setAttribute('class', 'preview-image'); 11 previewImage.setAttribute('src', blob); 12 13 // 生成したHTMLの要素をブラウザに表示させる 14 previewWrapper.appendChild(previewImage); 15 previewList.appendChild(previewWrapper); 16 };
上記の6行目のように画像にも「data-index」を付与しています。
問題発現までの流れ
新規投稿する際に、今までの機能では画像を選択したところでプレビュー画像が現れない仕様でした。
そこで、まずは画像一枚のみ現れるように新しくpreview.jsを作り、そこにコードを記述しました。
その後に複数画像を投稿できるようにまずは一枚目の画像を選択すると2枚目専用の画像選択ボタンが現れるようにしました。次に2枚目の画像が選択されると、その画像が現れるようにしました。その時に、現れた画像に番号をつけ、画像の変更・削除に対応するようにしましたが、ブラウザの検証で見てみると、それぞれの画像にはdata-index(※画像に番号をつけるための変数)が振り分けられているのに、全ての画像の番号が0(最初の番号)になっている状態になりました。
ちなみに画像選択ボタンの方はそれぞれしっかりと0番から順番に振り分けられていましたので、そこは問題ありませんでした。
試したこと
data-indexがどこに紐づいているか、抜けているところはないかチェックしてみた。
補足情報
念の為、ファイル全体をここに掲載します。
preview.js
1document.addEventListener('DOMContentLoaded', function(){ 2 // 新規投稿・編集ページのフォームを取得 3 const learnForm = document.getElementById('new_learn'); 4 // プレビューを表示するためのスペースを取得 5 const previewList = document.getElementById('previews'); 6 // 新規投稿・編集ページフォームがないならここで終了 7 if (!learnForm) return null; 8 9 // プレビュー画面を生成・表示する関数 10 const buildPreviewImage = (dataIndex, blob) =>{ 11 // 画像を表示するためのdiv要素を生成 12 const previewWrapper = document.createElement('div'); 13 previewWrapper.setAttribute('class', 'preview'); 14 previewWrapper.setAttribute('data-index', dataIndex); 15 16 // 表示する画像を生成 17 const previewImage = document.createElement('img'); 18 previewImage.setAttribute('class', 'preview-image'); 19 previewImage.setAttribute('src', blob); 20 21 // 生成したHTMLの要素をブラウザに表示させる 22 previewWrapper.appendChild(previewImage); 23 previewList.appendChild(previewWrapper); 24 }; 25 26 // file_fieldを生成・表示する関数 27 const buildNewFileField = () => { 28 // 2枚目用の画像選択ボタンを作成 29 const newImageFile = document.createElement('p'); 30 newImageFile.innerHTML = "画像を選択する"; 31 // 2枚目用のfile_fieldを作成 32 const newFileField = document.createElement('input'); 33 newFileField.setAttribute('class', 'hidden'); 34 newFileField.setAttribute('type', 'file'); 35 newFileField.setAttribute('name', 'learn[images][]'); 36 37 // 最後のfile_fieldを取得 38 const lastFileField = document.querySelector('input[class="hidden"][type="file"][name="learn[images][]"]:last-child'); 39 // nextDataIndex = 最後のfile_fieldのdata-index + 1 40 const nextDataIndex = Number(lastFileField.getAttribute('data-index')) +1; 41 newImageFile.setAttribute('data-index', nextDataIndex); 42 newFileField.setAttribute('data-index', nextDataIndex); 43 44 // 追加されたfile_fieldにchangeイベントをセット 45 newImageFile.addEventListener("change", changedFileField); 46 newFileField.addEventListener("change", changedFileField); 47 48 // 生成したfile_fieldを表示 49 const fileFieldArea = document.querySelector('.click-upload'); 50 fileFieldArea.appendChild(newImageFile); 51 fileFieldArea.appendChild(newFileField); 52 }; 53 54 // input要素で値の変化が起きた際に呼び出される関数の中身 55 const changedFileField = (e) => { 56 // data-index(何番目を操作しているかを取得) 57 const dataIndex = e.target.getAttribute('data-index'); 58 console.log(dataIndex); 59 60 const file = e.target.files[0]; 61 const blob = window.URL.createObjectURL(file); 62 63 buildPreviewImage(dataIndex, blob); 64 buildNewFileField(); 65 }; 66 67 // input要素を取得 68 const fileField = document.querySelector('input[type="file"][name="learn[images][]"]'); 69 70 // input要素で値の変化が起きた際に呼び出される関数 71 fileField.addEventListener('change', changedFileField); 72}); 73
対象のhtmlコードを追記します。
_form.html.erb
1<%= form_with model: @learn, id: 'new_learn', local: true do |f| %> 2<div class="learn-form-group"> 3 <div class="weight-bold-text"> 4 タイトル 5 <span class="indispensable">必須</span> 6 </div> 7 <%= f.text_area :title, class:"learns-title", id:"learn-title", placeholder:"40文字まで", maxlength:"40" %> 8</div> 9 10<div class="learn-form-group"> 11 <div class="weight-bold-text"> 12 説明文 13 </div> 14 <%= f.text_area :description, class:"learns-description", id:"learn-info", placeholder:"1000文字まで", maxlength:"1000" %> 15</div> 16 17<div class="learn-form-group"> 18 <div class="weight-bold-text"> 19 教科 20 <span class="indispensable">必須</span> 21 </div> 22 <%= f.collection_select(:subject_id, Subject.all, :id, :name, {}, {class:"select-box", id:"learn-subject"}) %> 23</div> 24 25<div class="learn-form-group"> 26 <div class="weight-bold-text"> 27 学習時間 28 </div> 29 <div class="learns-time"> 30 <div class="time-form"> 31 <%= f.text_field :study_hour, class:"learn-time", id:"study-hours" %><span>時間</span> 32 </div> 33 <div class="time-form"> 34 <%= f.text_field :study_minutes, class:"learn-time", id:"study-minutes" %><span>分</span> 35 </div> 36 </div> 37</div> 38 39<div class="learn-form-group"> 40 <div class="image-title"> 41 画像 42 </div> 43 <label class="form-image"> 44 <div id="previews"></div> 45 <div class="click-upload"> 46 <p> 47 画像を選択する 48 </p> 49 <%= f.file_field :image, name: 'learn[images][]', class: "hidden", data: {index: 0} %> 50 </div> 51 </label> 52</div> 53 54<div class="learn-post-btn"> 55 <%= f.submit "記録する", class:"post-btn" %> 56</div> 57<% end %>
恐縮ですが、ご回答していただける方お待ちしております。
