How-To: Simple JavaScript image preview


Recently, while doing some usual front-end stuff around, I flew into a situation which (surprisingly to me) I had never encountered. It was a simple form for image upload and, after interacting with it, I realized I needed to have live-preview of the image that has just been attached to the form field.

I knew it would be an easy-peasy job to implement such functionality, but I needed an elegant (some geeks may even call it “sexy”) solution. After rustling through the Internet, I combined two of the most widely-spread solutions, adapted them to my needs, polished them and the result was (not only truly simple but also) beautiful. So, in short – how can we easily implement a live-preview of an image using JavaScript and jQuery?

The Prerequisits

It is assumed that we have got jQuery injected into our markup (the version is of your choice, since we’re just using jQuery’s element selector and attribute modification mechanisms). So, in the <head> tag of our HTML markup file, we need to have a <script> tag injecting jQuery (the example makes use of the jQuery CDN):
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>

The Markup

The pure HTML markup used for our demo purposes can be pretty simple and straightforward – we need a form with an <input type=”file” /> field, optionally provided with a simple file type validation in the form of accept attribute (really hackable in a real-life situation, but perfect for our demo purposes). Don’t forget that, in the real world, you may not miss some vital <form> attributes such as action, method and, most important of them all, enctype=”multipart/form-data” (since you will have file upload fields). We, however, don’t need these for now. So, again, the markup:

...
<body>
    <form>
        <input id="image-input" type="file" accept=".png, .jpg, .jpeg">
    </form>
    <img id="image-preview" alt="No Preview"/>
</body>
...

The JavaScript

The attachment image preview functionality itself needs to be implemented in a separate <script> tag, recommended in the <head> tag of our markup, but, of course, after the tag that injects jQuery (because we need jQuery to be already available for our script). The JavaScript code can, of course, be extracted to a separate external file, but if you do this, please again make sure you refer the file, e.g. <script src=’image-preview.js’></script> after you have injected jQuery.

Furthermore, inside the <script> tag denoted for our image preview logic (or inside the external file), we must first create a jQuery wrapper function, because we’re using jQuery’s functionalities for DOM element fetching:

jQuery(function($) {
    //our code goes here
});

Then, for such small functionalities, I prefer to move out the DOM element attribute names, IDs and classes to separate “global JavaScript pseudo-constants” (as I call them) in the form of variables in the beginning of our jQuery function:

var FILE_INPUT_FIELD_DOM_ID = 'image-input';
var THUMBNAIL_IMAGE_DOM_ID = 'image-preview';
var THUMBNAIL_IMAGE_ATTRIBUTE_SRC = 'src';

Now we come to the essence – two functions which we want to perform the actual preview. The first is named  showPreview(fileInputElement) and, as the name states, it shows our image. The second is named accommodateImagePreviewInDOM(extractedDataEvent) and it does the DOM manipulations after the image data is extracted from the attached file (respectively the input file DOM element passed as an argument to the showPreview(fileInputElement) function has been processed). In short, showPreview(fileInputElement)  makes use of the native JavaScript FileReader   to extract the data of the image that is attached to the file input field and then calls accommodateImagePreviewInDOM(extractedDataEvent) which appends the Base64 extracted image data as a source attribute to our <img /> preview markup tag.

Inside the showPreview(fileInputElement) function we do the following things:

  • Firstly check that our passed DOM element object has a files’ array and if at least one file is attached.
    if (fileInputElement.files && fileInputElement.files[0]) { ...
  • If so, we create a new FileReader() object which is to be used for extracting the file data of the image attached to the input field.
    var reader = new FileReader();
  • Later on, we invoke the file reader to read the data considering our attached image as data URL (which reads the contents of the specified file and, once finished, the result attribute contains a data: URL representing the file’s data).
    reader.readAsDataURL(fileInputElement.files[0]);
  • Finally, we trigger the reader.onload event (a handler event, triggered after the previously mentioned file data reading operation is successfully completed), attach a callback function to it which gets the event as an argument and inside it call accommodateImagePreviewInDOM(extractedDataEvent) where extractedDataEvent is the actual reader.onload event. In other words, we say: “after having successfully read the file data, invoke the method which uses this data to visualize it in the DOM and, of course, provide it with the data when calling it”.
     
    reader.onload = function(e) {
        //We must pass the event reference holding the image data
        accommodateImagePreviewInDOM(e);
    }

The accommodateImagePreviewInDOM(extractedDataEvent) is actually pretty simple – it uses jQuery to select the <img /> tag which is denoted to hold our preview and sets its source attribute to contain the image data received as extractedDataEvent, which is exactly carried in the extractedDataEvent.target.result field of the event object.

function accommodateImagePreviewInDOM(extractedDataEvent) {
    $('#' + THUMBNAIL_IMAGE_DOM_ID).attr(THUMBNAIL_IMAGE_ATTRIBUTE_SRC, extractedDataEvent.target.result);
}

Finally, at the end of our script, we must (using jQuery) attach an event to the input file field which triggers the preview action when changed (in other words, when an image is attached, invoke the logic which displays the image). Here the important thing is that we must send a pure DOM element to the showPreview(fileInputElement) function, since the FileReader expects so. If we send a jQuery wrapped element in the form of $(fileInputElement), FileReader won’t work properly. So, our triggering lines may look like this:

$('#' + FILE_INPUT_FIELD_DOM_ID).change(function() {
    //!NB We must pass the DOM (this) object and NOT
    //the DOM object with the jQuery wrapper ($(this))
    showPreview(this);
});

 

We’re done! Our preview functionality now works like a charm! To polish our demo a little bit, we can give some CSS styling in a simple <style> tag placed in the markup <head>.

Here is the whole demo code:

<!DOCTYPE HTML>
<html lang="en">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
        <title>Image Preview Demo</title>
        <style>
            body {
                font-family: Calibri;
                font-size: 18px;
            }

            #image-preview {
                max-width: 250px;
                max-height: 250px;
                margin: 2%;
                padding: 3%;
                border: 1px solid #d8d8d8;
                border-radius: 3px;
            }
        </style>
        <script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>

        <!-- Keep in mind - our script is placed after we have injected jQuery! -->
        <script type="text/javascript">
            jQuery(function($) {
                var FILE_INPUT_FIELD_DOM_ID = 'image-input';
                var THUMBNAIL_IMAGE_DOM_ID = 'image-preview';
                var THUMBNAIL_IMAGE_ATTRIBUTE_SRC = 'src';

                function showPreview(fileInputElement) {
                    if (fileInputElement.files && fileInputElement.files[0]) {
                        var reader = new FileReader();

                        reader.readAsDataURL(fileInputElement.files[0]);

                        reader.onload = function(e) {
                            //We must pass the event reference holding the image data
                            accommodateImagePreviewInDOM(e);
                        }
                    }
                }

                function accommodateImagePreviewInDOM(extractedDataEvent) {
                    $('#' + THUMBNAIL_IMAGE_DOM_ID).attr(THUMBNAIL_IMAGE_ATTRIBUTE_SRC, extractedDataEvent.target.result);
                }

                $('#' + FILE_INPUT_FIELD_DOM_ID).change(function() {
                    //!NB We must pass the DOM (this) object and NOT
                    //the DOM object with the jQuery wrapper ($(this))
                    showPreview(this);
                });
            });
        </script>
    </head>
    <body>
        <form>
            <input id="image-input" type="file" accept=".png, .jpg, .jpeg">
        </form>
        <img id="image-preview" alt="No Preview"/>
    </body>
</html>

You can try the demo live here.

Cheers,

Alex


Leave a comment

Your email address will not be published. Required fields are marked *