0

I do welcome constructive criticism and suggestions for different methods of completing this task.

I'm trying to write some jQuery that will allow users to preview a file or multiple files within the current window without the DOM being reloaded.

To achieve this, the simplest way I know, to .append() an image element within <div id="gallery">. I found myself struggling to get the file names to be displayed along with the correct picture, the main issue was that the pictures rendered in no particular order (probably whichever file was smallest was rendered first).

Luckily I stumbled across this post, HTML5 FileReader how to return result? and managed to adapt the code so that it rendered the picture instead of the base64 encoding.

$(function(){
  $('#file_input').change(function(e){
      var files = $(this.files)
      $(this.files).each(function(i){
         readFile(files[i], function(e) {
             var imageSrc = e.target.result
             $('#output_field').append('<h4>'+files[i].name+'</h4><img class="preview-thumbs" id="gallery-img" src="' + imageSrc + '">');
            })
        });
    });
  function readFile(file, callback){
     var reader = new FileReader();
     reader.onload = callback
     reader.readAsDataURL(file);
  }
});
.preview-thumbs {display: block; padding: 10px 0px; width: 250px;}
.thumb-containers {}
#gallery>.img-container {display: inline-block; border: 3px solid #243d51; padding: 5px; width: 350px; border-radius: 20px; text-align: center;}
h4 {color: red; font-size: 20px; font-weight: bold;}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<input type="file" id="file_input" class="foo" multiple/>
<div id="output_field" class="foo"></div>

My question here is:

Is there (if any) better way of completing this task?

Cheers in advance, Swift

Swift
  • 1,663
  • 1
  • 10
  • 21
  • 1
    Seems fine to me. Is there anything in particular you don't like about the solution? I could see (super) large images being a problem but I doubt it matters. – Halcyon Jul 17 '18 at 16:08
  • 1
    Instead of `$(this.files).each(...)` use `files.each(..)`, this is unnecessary duplication. – Chayim Friedman Jul 17 '18 at 16:10
  • 1
    And instead of `files.each(function(i) { readFile(files[i], ...); });`, simply use `files.each(function(i, file) { readFile(file, ...); });`. – Chayim Friedman Jul 17 '18 at 16:12
  • @חייםפרידמן many thanks for pointing this out, it is exactly this kind of feedback I am looking for. I am by no means an expert but duplication is never a good thing :) – Swift Jul 17 '18 at 16:15
  • @Halcyon whilst I appreiciate that large files could potentially cause a problem, I have specified a small snippet of CSS which I will post here `.preview-thumbs {display: block; padding: 10px 0px; width: 250px;}` This means it will automatically scale the images height based on the width parameter. (At least I think that is how it works lol!) – Swift Jul 17 '18 at 16:17
  • @user9569124 I'm sorry, I should have been more specific. I mean large in terms of memory usage. If I had a 2GB file the browser would probably crash trying to load it. You could add a filesize check with a sensible limit. – Halcyon Jul 17 '18 at 20:07

2 Answers2

1

I recently publish a project that solves the exactly same problems.

I manage the file upload in a separate class that also covers Drag / Drop. Basically, you must return target.result on the "load" event.

const fileReader = new FileReader();
fileReader.addEventListener("load", this.fileReader_load.bind(this, file.name), false);
fileReader.readAsDataURL(file);


fileReader_load(fileName, event) {
    event.target.removeEventListener("load", this.fileReader_load);
    this.onFileLoaded(fileName, event.target.result);
}  

See the full image loader here: https://github.com/PopovMP/image-holder/blob/master/public/js/file-dropper.js

The image preview is easy. Make an image element and set its src to the imageData.

Here is the full source code: https://github.com/PopovMP/image-holder

Miroslav Popov
  • 3,294
  • 4
  • 32
  • 55
  • Hi there, what is the `const` for in the first line? – Swift Jul 17 '18 at 16:21
  • `const` is introduced in ES6 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const). It guarantees that the variable is immutable. You may use let or var with the same result. I use `const` to make my code more "pure". It is very subjective, but if, I have to use `let`, it means that the code can be improved. – Miroslav Popov Jul 17 '18 at 17:02
  • Having a brief read of that page you linked, I can see why one would desire such a type of variable, one that cannot change, at least without going to great lengths. – Swift Jul 17 '18 at 19:25
0
$(function(){
    $('#file_input').change(function(e){
        var files = $(this.files)
        $(files).each(function(i, file){
        readFile(file, function(e) {
            var imageSrc = e.target.result
            $('#output_field').append('<div class=""img-container"> <h4>'+file.name+'</h4><img class="preview-thumbs" id="gallery-img" src="' + imageSrc + '"/></span>');
        })
    });
});
    function readFile(file, callback){
    var reader = new FileReader();
    reader.onload = callback
    reader.readAsDataURL(file);
    }
});

This is the jQuery code amended as per the suggestion in the comments about duplication. If you post that as an answer, I will accept it :)

Swift
  • 1,663
  • 1
  • 10
  • 21