shokun1209
@shokun1209 (s shoya)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Javascript プレビュー画像のremoveがうまくできない

解決したいこと

ruby on rails にて画像投稿時のプレビュー機能を実装しております。

プレビュー.jpg

このように複数画像を表示させる事はできたのですが、画像が表示されている状態で
再度画像を選び直した場合に、既に表示されていた画像を全てremoveさせたい。

発生している問題

画像が既に複数枚プレビュー表示されている状態で再度画像選択を行った場合、
表示されているプレビューの一番右側の画像のみがremoveされ、その他のプレビュー画像が残ったままとなってしまう。

【現状説明】
[A] [B] [C]
上記のようにABCの3枚の画像がプレビュー表示されている状態で、再度画像D 1枚を選択し直す

【理想】
[D]
上記のように、ABCの3枚のプレビューが画面上から消え、新たに選択した画像Dのみがプレビューされる

【現実】
[A] [B] [D]
画像Cだけがremoveされ、ABがプレビューに残ってしまう。
※file_fieldには[D]のみが登録されている。

該当するソースコード

<%# new.html.erb %>

<div class="topic-new">
  <div class="form-main">
    <%= form_with model:@topic, local: true do |f| %>
      <%= render 'shared/error', model: f.object %>
      <label>
      <%= f.file_field :images, multiple: true, id: :topic_image, class:"f-file" %>
      <div class="previews" id="image-list">
        <div class="previews-text">画像を選択</div>
      </div>
      </label>

      <%= f.collection_select(:category_id, Category.all, :id, :name, {},{class: "f-category form"}) %>
      <%= f.text_field :title ,placeholder: "20文字以内", class:"f-title form" %>
      <%= f.text_area :text ,placeholder: "1000文字以内", class:"f-text form" %>
      <div class="checkbox">
      <%= f.label :anonymous, "匿名表示にする" %>
      <%= f.check_box :anonymous ,{checked: true}, "true", "false" %>
      </div>
      <div class="f-bottom">
      <%= f.submit "投稿", class:"f-submit form" %>
      </div>
      <div class="rootpath">
      <%=link_to 'ホームへ戻る', root_path, class:"rootpath-btn" %>
      </div>
    <% end %>
  </div>
</div>

画像ファイルは一度に複数選択可能で、配列でDBへ保存されます。

// preview.js

document.addEventListener('DOMContentLoaded', function(){
  if ( document.getElementById('topic_image')){
    const ImageList = document.getElementById("image-list");
    document.getElementById("topic_image").addEventListener("change",function(e){

      // 問題となっているremoveの記述
      let imageContent = document.querySelector("#currentThumb");
      if (imageContent){
        imageContent.remove();
      };

      const file = e.target.files;
      const num = file.length;

      // 一度に複数プレビューする為・枚数制限の為の記述
      for (let i = 0 ; i < num ; i++ ){
        const blob = window.URL.createObjectURL(file[i]);
        if (num > 4){
          alert("画像は4枚まで投稿できます。")
          return false;
        }
        // 画像を表示するためのdiv要素を生成する記述
        const imageElement = document.createElement('div');
        imageElement.setAttribute('class',"topic-preview");
        imageElement.id = "currentThumb"  // ここでremoveの為のIDを設定

        // 表示する画像を生成する記述
        const blobImage = document.createElement('img');
        blobImage.setAttribute('src', blob);
        blobImage.height = 100;
        blobImage.width = 80;

        // 生成したHTMLの要素をブラウザに表示させる記述
        imageElement.appendChild(blobImage);
        ImageList.appendChild(imageElement);
      };
    });
  };
});

自分で試したこと

let imageContent = document.querySelector("#currentThumb");

querySelectorを('img')としても同様に1枚のみremoveされる状況です。
querySelectorAll()とすると
「Uncaught TypeError: imageContent.remove is not a function」
のエラーが出力されてしまい、プレビューされている全ての画像をどのように設定するのかが
分からず泣いています:cry:

何卒よろしくお願い致します。

0

2Answer

querySelector()querySelectorAll()などの関数に対する理解と、HTMLのclassやidとそれに対するセレクタについても理解する必要があります。

Document.querySelector()

Document の querySelector() メソッドは、指定されたセレクターまたはセレクターのグループに一致する、文書内の最初の Element を返します。一致するものが見つからない場合は null を返します。

セレクターにマッチする最初のElement、つまり1つしか取得できないので、画像が1枚のみremoveされるのは自然な動作です。

Document.querySelectorAll()

Document の querySelectorAll() メソッドは、与えられた CSS セレクターに一致する文書中の要素のリストを示す静的な (生きていない) NodeList を返します。

Elementを1つだけ返すquerySelector()に対して、こちらはNodeListを返します。

NodeListにはremove()はありませんので「remove is not a function」というエラーになります。

#currentThumbはidを指定するものですが、idは一意でなければならないので、複数の要素に同じidを付けても期待した動作にならないと思います。

解決方法としては次のどちらかになると思います。

  1. querySelector()で1つずつ要素を取得して削除する
  2. querySelectorAll()でまとめて要素を取得し、ループ処理で1つずつ削除する
2Like

Comments

  1. @shokun1209

    Questioner

    ご回答頂きありがとうございます!!

    @blue32a 様に教えて頂いた内容を元に私なりに書いてみたところ、思い通りの実装をする事ができました。

    大変助かりました!!

コメントにて丁寧に教えて頂いたおかげで、思い通りの実装をする事ができました。

初学者のめちゃくちゃなコードだと思いますが、一応変更後のコードを一応載せておきます。

      const imageContents = document.querySelectorAll('#currentThumb');
      const count = imageContents.length
      const imageContent = document.querySelector('#currentThumb');
      if (imageContent){
        for (let i = 0 ; i < count ; i++){
          const removeContent = document.querySelector('#currentThumb')
          removeContent.remove();
        };
      };
0Like

Your answer might help someone💌