0

In the website is the ability to ask a question and provide answers with images. We have used some website template and now trying to change functionallity. So I'm trying to achieve if image clicked, pop ups upload window (to select picture) and after clicking ok button, it should be shown near the answer (look image below, noted in red).

answers

There is the ability to add more input fields by clicking the button. It generating HTML like this:

<div class="row">
   <div class="another-div">
      <div class="image-answer">
         <input type="image" name="answers_images[]" id="test-1" src="image.png">
         <input type="file" name="answers_images[]" id="file-1" style="display: none;">
      </div>
      <label class="text-answer">
         <input type="text" name="answers[]" placeholder="Type here your answer">
      </label>
   </div>
</div>

If user clicks the button it will add another row with incremented ids, so it will be test-2, file-2 after clicking button once again it will add row with incremented ids once again test-3, file-3 and so on.

How can I detect input clicks (via JQuery) if ids are auto incremental? And after it do some changes with JQuery only with clicked ($this) image.

Something like this:

$("#file-1...").click(function(e) {
    e.preventDefault();        
    // Some code here, I have working code to achieve what I want
    // But problem, that I have no idea how to detect those clicks with dynamic ids
});

$("#test-1").change(function(e) {
    e.preventDefault();
});
Infinity
  • 828
  • 4
  • 15
  • 41
  • 1
    Give similar elements a `class`. Handle events based on this `class` and with that `$(this)` would be the unique element. Got it? – emerson.marini Sep 24 '20 at 08:26
  • Does this answer your question? [Event binding on dynamically created elements?](https://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements) – freedomn-m Sep 24 '20 at 10:17

2 Answers2

2

If you want to do event handling on dynamically added content, the trick is using the .on() method with a non-dynamically added parent element, and providing a selector argument. This approach use classes instead of ids. You cans still have ids, but the this variable becomes the element selected, so you don't really need them.

<div class="parent">
  <div class="image-answer dynamically-added" id="answer1"></div>
  <div class="image-answer dynamically-added" id="answer2"></div>
  <div class="image-answer dynamically-added" id="answer3"></div>
</div>

$('.parent').on('click', '.image-answer', function() {});

Say if you clicked on the second .image-answer, in the event handler function this would be the same as $('#answer2')[0]. So to make it a jquery object you just have to $(this). And if you want to access a child elemet, just use the .find() method.

$(this).find('input[type="file"]').on('change', function previewFile() { ... })

Also, inputs dont' have a default action for click, so e.preventDefault() isn't necessary.

PoorlyWrittenCode
  • 1,003
  • 1
  • 7
  • 10
  • This is also explained here: https://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements – freedomn-m Sep 24 '20 at 10:18
  • Also `input type=image` is a submit button, so will submit if you don't preventDefault https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/image – freedomn-m Sep 24 '20 at 10:19
  • I'm sure this has been answered many times on stackoverflow. I know I've answered it a few times myself. But I try to answer it in the context of the question. My goal is to help @Infinity understand what's going on, not just provide copy pasta. Also, you're correct, I was thinking `` when I mention it doesn't need `preventDefault()`. But maybe it'll help someone realize you don't need to call this method for every event handler. – PoorlyWrittenCode Sep 24 '20 at 23:59
  • I do not think this is a duplicate question at all - This is not just only what OP wanted because if you go through the complete question there are a lot of things which should he want considered and he wanted to do that as well which i have covered very briefly in my answer and provided a fully working answer to what OP wanted. – Always Helping Sep 25 '20 at 01:16
  • @AlwaysHelping that's why it a) requires more than 1 vote and b) now makes it a suggestion "does this answer". :) It might well have answered the question and frequently it does, perhaps in this case additional details are required. That's also why I put "this is also explained" (where it's already explained and better) rather than "you should use this answer" - the comment here was providing *additional* information. – freedomn-m Sep 25 '20 at 07:30
  • @freedomn-m Indeed I agree on the `event delegation` side of this question but other things are mentioned by OP as well which OP wanted addressed as well :) – Always Helping Sep 25 '20 at 07:33
  • Thank you very much for the detailed explanation. It was useful to read. – Infinity Oct 01 '20 at 12:24
1

You can use class selector instead of id's to have a more dynamic approach of what you are trying to achieve. Also with class you will be writing less code and getting the same results.

You can still use the id's increment system to make sure the id's are unique in the DOM.

Add class to your input image and input file and and watch a change on those classes. You can use prev() method you select the previous input of which the change was made on using $(this)

//Load preview image on each answer
$(document).on('click', '.add_image', function(e) {
  e.preventDefault()
  //add file to input / trigger click
  $(this).next('input').click();
})

//Watch the change on select file
$(document).on('change', '.select_file', function(e) {
  e.preventDefault()
  //get the sibling img src of the change file
  var prevInput = $(this).prev('input')
  //Call read file preview
  previewFile(this, prevInput);
});

Pass your input and prev input image where the change happened to your previewImage function so that the file can be previewed in the image location using file Reader API.

//Preview img
function previewFile(input , previewSrc) {
    if (input.files && input.files[0]) {
    var reader = new FileReader();
    reader.onload = function(e) {
        //load file into actual preview img
      $(previewSrc).attr('src', e.target.result);
    }
    reader.readAsDataURL(input.files[0]); // convert to base64 string
  }
}

I have added the functionality of adding more answers as well and you can upload image and preview images on those new answers as well using event Delegation as the elements are being append dynamically.

Complete Working Demo:

//row with incremented ids
var counter = 2

//Add more
$('#add_more').click(function() {
  //add new answer
  var newRow = `<div class="another-div">
      <div class="image-answer">
         <input type="image" class='add_image' name="answers_images[]" id="test-` + counter + `" src="https://img.icons8.com/dotty/80/000000/upload.png">
         <input type="file" class='select_file' name="answers_images[]" id="file-` + counter + `" style="display: none;">
      </div>
      <label class="text-answer">
         <input type="text" name="answers[]" id="answer-` + counter + `" placeholder="Type here your answer">
      </label>
      <button class="remove_answer"><i class="fas fa-window-close"></i></button>
   </div>`
  //append new answers
  $('.row').append(newRow)
  counter++ //increare counter
})

//remove answer
$(document).on('click', '.remove_answer', function() {
  //remove the answer div
  $(this).parent().remove()
  counter-- //decrease counter
})

//Load preview image on each answer
$(document).on('click', '.add_image', function(e) {
  e.preventDefault()
  //add file to input / trigger click
  $(this).next('input').click();
})

//Watch the change on select file
$(document).on('change', '.select_file', function(e) {
  e.preventDefault()
  //get the sibling img src of the change file
  var prevInput = $(this).prev('input')
  //Call read file preview
  previewFile(this, prevInput);
});

//Preview img
function previewFile(input, previewSrc) {
  if (input.files && input.files[0]) {
    var reader = new FileReader();
    reader.onload = function(e) {
      //load file into actual preview img
      $(previewSrc).attr('src', e.target.result);
    }
    reader.readAsDataURL(input.files[0]); // convert to base64 string
  }
}
.add_image {
  width: 80px;
  height: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://kit.fontawesome.com/a076d05399.js"></script>
<div class="row">
  <div class="another-div">
    <div class="image-answer">
      <input type="image" class='add_image' name="answers_images[]" id="test-1" src="https://img.icons8.com/dotty/80/000000/upload.png">
      <input type="file" class="select_file" name="answers_images[]" id="file-1" style="display: none;">
    </div>
    <div id="preview_img"></div>
    <label class="text-answer">
         <input type="text" name="answers[]" id="answer-1" placeholder="Type here your answer">
      </label>
  </div>
</div>
<br>
<button id="add_more">
  Add New Answer
</button>
Always Helping
  • 14,316
  • 4
  • 13
  • 29
  • 1
    Thank you so much, it not just worked, but also I've understood how it's working. Thank you once again! – Infinity Oct 01 '20 at 12:23