0

I have a gallery in a page where you click plus and minus to add and remove gallery items to which you then add a file to the input for that newly appended item.

I have created a file reader to get the files url but it only works on the first element on that page, anything added after that dynamically is not affected.

here is my JS:

$('#gallery .gallery-item .file_upload').each(function() {
    var $this = $( this );
    //$('body').on('click', 'input', function() {
    $this.on( 'change', 'input', function(evt){
        var files = evt.target.files;
        var file = files[0];
        console.log(files[0]);
        var reader = new FileReader();
        reader.onload = (function(file) {
            return function(e) {
                console.log(e.target.result);
            };
        })(file);
        reader.readAsDataURL(file);
    });
  });

The append gallery item function:

$('#gallery')
    .on('cocoon:before-insert', function(e,image_field) {
        image_field.insertAfter( $(this).next() );
    })
    .on('cocoon:after-remove', function(e, image_field) {
        $(this).data('remove-timeout', 1000);
        image_field.fadeOut('slow');
});

    $('.gallery-item').each(function() {
        $(this).next('a.add_fields').appendTo(this);
        $(this).next('input').appendTo(this);
    });

HTML:

<div class="image-field">
            <div class="file_upload">
                <input class="image_file" id="gallery_files" name="book[gallery_attributes][images_attributes][1412768497650][file]" type="file">
            </div>
        </div>

Can anyone tell me why the .each function will not work with this .on function for the newer items that are appended. I think it is the top most function thats obviously not working here and not the others below.

M dunbavan
  • 1,157
  • 5
  • 22
  • 57

2 Answers2

1

You've correctly identified that you need to use event delegation because you have dynamically added elements on your page. The problem is that event delegation works on an element that exists when the code runs, and you're using an .each() loop to iterate over a collection that you expect to contain your dynamically added elements (which it won't because they don't currently exist).

Essentially, the problem is the initial selector $('#gallery .gallery-item .file_upload').

The .gallery-item .file_upload part of that selector is part of what identifies the dynamic elements for event delegation, so you need to use something more like this:

$('#gallery').on('change', '.gallery-item .file_upload input', function(e) {
    // your code here
});

I've taken out the call to .each() because it's redundant; .on() iterates over the set of matched elements already, and your $('#gallery') selector should only match a single element anyway.

Note: You've used .gallery-item in the selector, but there's only an image-field class in the HTML you provided. That may or may not be an issue depending on exactly what the page looks like, since you haven't provided that information I've had to guess.

Anthony Grist
  • 38,173
  • 8
  • 62
  • 76
  • That worked yes, it was the order of delegation so it starts from the id of the parent div and the .on basically runs through each child inside that of `.gallery-item .file_upload input` and then runs the `.on('change')`. Cool thanks for the explanation Anthony. :) – M dunbavan Oct 08 '14 at 12:08
0

Why are you even using .each() ? All you need to make it work is using the selector since jQuery already will loop all of the selected items to .on()

$('#gallery .gallery-item .file_upload').on( 'change', 'input', function(evt){
        var files = evt.target.files;
        var file = files[0];
        console.log(files[0]);
        var reader = new FileReader();
        reader.onload = (function(file) {
            return function(e) {
                console.log(e.target.result);
            };
        })(file);
        reader.readAsDataURL(file);
});

The problem is that each is taking the curently existing inputs from the gallery unless you are looping everytime you are appending new items but then you will bind the handler alot of times on the same element.

Spokey
  • 10,974
  • 2
  • 28
  • 44
  • Spokey yeah so I press a plus button and it adds a new field, then to add the image onto the input I click upload image. That newly appended file field is added next to the first field that was loaded onto the page. When I try this it only reads it from that first item still – M dunbavan Oct 08 '14 at 12:02
  • Ah try changing the selector `$('#gallery .gallery-item')`. Didn't quite understand which items are dynamically added, your selector should be the parent on the page that is static and is there from the start. – Spokey Oct 08 '14 at 12:04
  • 2
    @WouterFlorijn No, you don't need to add the event listener to them - that's precisely why event delegation exists. The problem is, as stated in my answer, his selector for the `.each()` is too specific and the selector for the `.on()` call might not be specific enough (or would work completely fine depending on his exact HTML). – Anthony Grist Oct 08 '14 at 12:08
  • @WouterFlorijn the form (with the parameters) in which `.on()` is used here makes it work with dynamically added elements see http://stackoverflow.com/q/203198/2220391 – Spokey Oct 08 '14 at 12:12
  • I see. Well I learned something new about jQuery today :) sorry for being misleading then. – Wouter Florijn Oct 08 '14 at 12:14