質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

1回答

493閲覧

JavaScriptを利用した複数画像投稿機能で、画像にうまく番号が付けられない!

Mikoto

総合スコア2

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2022/07/19 16:09

編集2022/07/21 05:41

前提

ミニアプリを作っておりまして、現在は画像投稿機能の部分において、複数画像を投稿できるような機能を作っているところです。

実現したいこと

  • プレビュー表示において、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 %>

恐縮ですが、ご回答していただける方お待ちしております。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Lhankor_Mhy

2022/07/20 00:52

ご提示のコードを試してみましたが、data-index に与えられる番号はインクリメントされていました。つまり、問題が再現しませんでした。 おそらく、ご提示の部分には原因はなさそうに思います。 ご提示いただいていない部分に原因があるか、問題の共有が上手くできていないと思います。 問題再現の手順をもう少し詳しく書いていただけますか?
guest

回答1

0

HTMLをご提示いただいていないので、想像で補って回答します。

html

1 <div id="new_learn"></div> 2 <div id="previews"></div> 3 <div class="click-upload"> 4 <input type="file" name="learn[images][]" class="hidden" data-index="0"> 5 </div>

↑これでは問題が再現しませんでした。
↓これですと問題が再現しました。

html

1 <div id="new_learn"></div> 2 <div id="previews"></div> 3 <div> 4 <input type="file" name="learn[images][]" class="hidden" data-index="0"> 5 </div> 6 <div class="click-upload"> 7 </div>

つまり、input[class="hidden"][type="file"][name="learn[images][]"]:last-childが複数存在するようなHTML構造になっているのではないかな? と想像します。

投稿2022/07/20 01:44

Lhankor_Mhy

総合スコア36074

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Mikoto

2022/07/20 07:54

ご回答ありがとうございます。説明不足でしたので、質問の方に説明を追記いたしました。ご確認よろしくお願いいたします。
Mikoto

2022/07/20 07:58

ご回答いただいた内容に関しては、他のファイルに同じ記述をしている場合も同様な問題が起きるということでしょうか?
Lhankor_Mhy

2022/07/20 08:59

ご提示のHTMLを試してみましたが、#new_learnが存在しないため、 if (!learnForm) return null; で早期リターンしているようです。 実際に試しているHTMLとは異なるのではないかな、と思います。 コードは、それだけで問題が再現するのか一度お試しになってからご提示いただいた方が、お互いに時間の無駄がなくなるのではないかな、と思います。
Mikoto

2022/07/21 05:47 編集

ご返信ありがとうございます。 new_learnが存在していないということについては、対象のhtmlのコードの先頭にあるform_with内のidとして設定しておりました。掲載していなかったこと、申し訳ありません。質問フォームにhtmlのコードを全体に変更致しましたのでご確認よろしくお願いいたします。 また、input[class="hidden"][type="file"][name="learn[images][]"]:last-child が複数存在するということについても気になりましたので、部分テンプレートを用いてnewアクションとeditアクションのテンプレートを統一しましたが、結果は変わりませんでした。 以上を踏まえて、恐縮ですがご確認よろしくお願いいたします。
Lhankor_Mhy

2022/07/21 08:55

Ruby は全くわからないので、erb形式のテンプレートはちょっと苦戦します。
Lhankor_Mhy

2022/07/21 09:18

試してみましたが、やはり問題が再現しませんでした。
Mikoto

2022/07/21 12:12

分かりました。お手数をおかけいたしまして申し訳ありません。 ここまでご対応いただき、ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問