Patrick Ponte
Patrick Ponte

Reputation: 25

How to change a specific image in a dynamic table?

I'm facing this strange behavior in one table and honestly I don't know what I'm missing here.

I've the following table:

<table>
<tr>
    <th>City</th>
    <th>Date</th>
    <th>Description</th>
    <th>Picture</th>
    <th>Sort</th>
    <th>
        <span class="table-add mb-3 mr-2">
          <a href="#!" class="text-success">
             <i class="fas fa-plus fa-2x program" aria-hidden="true"></i>
          </a>
        </span>
    </th>
</tr>
<tr>
    <td>Name</td>
    <td>01-01-20</td>
    <td>Lorem ipsum dolor sit amet.</td>
    <td>
        <div class="container-image">
            <div class="avatar-upload">
                <div class="avatar-edit">
                    <input type='file' id="imageUpload" accept=".png, .jpg, .jpeg" />
                    <label for="imageUpload"></label>
                </div>
                <div class="avatar-preview">
                    <div id="imagePreview" class="imagePreview" style="background-image: url(http://i.pravatar.cc/);">
                    </div>
                </div>
            </div>
        </div>
    </td>
    <td></td>
</tr>

I've an icon button to clone my last 'tr' and create a new one on the bottom of my table.

I also have a jquery function to upload a image to every single row, but for some reason when I change the image in row three for example, it will always update my first row.

<script>
function readURL(input) {
    if (input.files && input.files[0]) {
        var reader = new FileReader();
        reader.onload = function(e) {
            $('#imagePreview').css('background-image', 'url('+e.target.result +')');
            $(this).hide();
            $(this).fadeIn(650);
        };
        reader.readAsDataURL(input.files[0]);
    }
}
$("#imageUpload").change(function() {
    readURL(this);
});

I created a JSFiddle with a functional example of my table in order to give you an exact idea of this issue: https://jsfiddle.net/d95yo4vg/

Just add a new row and then try to change the image of the second row.

Upvotes: 0

Views: 176

Answers (2)

Wezelkrozum
Wezelkrozum

Reputation: 1006

There are several problems with your code. Let's go through them one at a time.

1. Clicking on the picture icon label does not open the upload dialog for the right input tag

When you add a new row you get these elements in your html:

<input type='file' id="imageUpload" accept=".png, .jpg, .jpeg" />
<label for="imageUpload"></label>

<input type='file' id="imageUpload" accept=".png, .jpg, .jpeg" />
<label for="imageUpload"></label>

Because we have multiple id attributes with the same value we violate the html5 spec: https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute And because there are multiple inputs with the same id, both labels will only target the first input. So we keep uploading files to the first input even when we click the label in the second row.

You can fix this by wrapping the label around the input so you do not need the for attribute anymore:

<label>
  <input />
</label>

2. You are only adding one event listener on the input at page load This is not a bug in your code, but this is a recommendation in case you ever add new elements to a page with javascript.

$("#imageUpload").change(...)

You are creating a change event listener on the input, but only once at page load. So if you ever add a new tr with the same elements by code (without a deep clone) there is no event listener on the input in the new row. It does work in your example because you create a deep clone of the tr. But it won't work for elements you load from an AJAX response and add to the document.

I would recommend you set an event listener on the document instead and target the imageUpload child. Because the document will always be there on the webpage and there will always be only one.

A code example of the document listener:

$(document).on("change", ".imageUpload", function(e) {
  readURL(e.currentTarget);
});

3. In the readURL function you are targeting the imagePreview in the first row

This problem has the same cause as the first problem, but with another solution. We have multiple img's with the imagePreview id. So the $('#imagePreview') selector will only return the imagePreview in the first row.

To select the imagePreview on the right row you can traverse up the dom to an element in the row with the closest() function. And traverse back to the imagePeview by using the find() function. An example:

$(input).closest('.avatar-upload').find('.imagePreview')

Info about closest: https://api.jquery.com/closest/

Info about find: https://api.jquery.com/find/


The full solution:

I've forked your fiddle example to a working example: https://jsfiddle.net/gkthp6qd/

Upvotes: 1

Kiran Shinde
Kiran Shinde

Reputation: 5992

The issue is you have used id as a selector i.e. #imagePreview

Try changing with

$('#imagePreview').css('background-image', 'url('+e.target.result +')');

To

  $(event.target).parents('.avatar-upload').find('.imagePreview').css('background-image', 'url('+e.target.result +')');

Id will always return first matched element

What I did is,

Catch the parent and then catch the imagePreview element

Upvotes: 1

Related Questions