Creating a drag and drop uploader with preview in Javascript
Home » BLOG » Web development » Creating a drag and drop uploader with preview in Javascript

Creating a drag and drop uploader with preview in Javascript

category:  Web development

A couple of weeks ago, I work on a project that has a preview image feature for the user. Basically, the user wants to upload the image and we want the user to see whether the uploaded image actually fits our card design or not. Once the user is happy with the image, the user can save the image. This preview feature helps users pick the perfect image.

What technologies and assets do we use in this tutorial

What the end result looks like

In the end, we will see the result of our code looks like this.

file drag and drop with preview image

Create an HTML file

First, we need to create an HTML file.

  • Open your code editor (VSCode)
  • Copy the HTML code below and paste it into your index file (index.html)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Drag and Drop with preview image in vanilla javascript by AppleRinquest.com</title>
    <!-- add our custom style -->
    <link rel="stylesheet" href="style.css">
    <!-- using Bootstrap framework -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <!-- using Font awesome framework -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
<body>

    <!-- 
        NOTE: 
        The code based can use for the multiple uploaded image inputs. 
        Each uploaded image input will have its own preview image card. 
        In this code, 

        We refer the elements by classname in Javascript. We don't use the element Id.
    -->

    <!-- Title -->
    <div class="container">
        <div class="row">
            <div class="col-12">
                <div class="mt-5"></div>
                <h3 class="h5 text-center text-secondary text-uppercase">Creating a drag and drop uploader with preview in Javascript</h3>
                <a href="http://applerinquest.com" class="d-block text-center text-decoration-none"><i class="fa-solid fa-arrow-up-right-from-square"></i> Apple Rinquest - WordPress/PHP developer</a>
            </div>
        </div>   
    </div>


    <!-- ### .preview-image-container | START ### -->
    <div class="container preview-image-container">
        <div class="row">
            <div class="col-md-6">
                <div class="mt-5"></div>
                <h2 class="h5 mb-2 pb-1">Upload file</h2>
                <p class="mb-4 fs-sm">File types supported: PNG, JPEG, JPG. Max size: 1mb.</p>

                <!-- invalid message -->
                <div class="invalid-feedback invalidFileSize" style="display:none;">The maximum file is 1mb. Please reduce the file size or change the new file.</div>
                <div class="invalid-feedback invalidFileType" style="display:none;">Only PNG, JPEG and JPG file type are acceptable. Please choose the new file type.</div>

                <!-- Drag and drop file upload -->
                <div class="file-drop-area">
                    <div class="file-drop-icon"><i class="fa-solid fa-cloud-arrow-up"></i></div>                    
                    <span class="file-drop-message">Drag and drop here to upload</span>
                    <input type="file" class="file-drop-input" accept=".png, .jpg, .jpeg">
                    <button type="button" class="file-drop-btn btn btn-primary btn-sm">Or select file</button>
                    <button type="button" class="btn btn-outline-secondary btn-sm remove-upload-btn">Remove</button>
                </div>      

                <!-- Note -->
                <div class="mt-5 text-muted">
                    <span><b>In this tutorial</b>, we want the users to view the uploaded image on the card design before saving.</span>
                </div>

            </div>

            <!-- preview image section -->
            <aside class="col-md-6 mt-0 text-center">
                <div class="mt-5"></div>

                <!-- preview image card -->
                <div class="card border-0 shadow bg-light d-inline-block" style="width: 400px; height: 600px; pointer-events: none; border-radius: 50px;">
                    <div class="d-flex align-items-end justify-content-start w-100 h-100">  

                        <!-- preview image -->
                        <img src="" alt="" class="position-absolute top-0 preview-image" style="display: none; border-radius: 50px; width: 400px; height: 600px; object-fit: cover;">

                        <!-- extra info -->
                        <div class="d-flex justify-content-between align-items-center mb-3 mx-5 mt-5 position-relative" style="z-index:100;">
                            <div class="d-inline-block w-50">
                                <span class="text-white bg-info bg-gradient rounded-pill p-2 preview-image-title"><i class="fa-solid fa-location-dot"></i>&nbsp;&nbsp;Krabi,Thailand</span> 
                            </div>
                            <div class="d-inline-block text-end w-50">
                                <img src="img/person-dummy.jpg" alt="https://www.freepik.com/drobotdean" class="rounded-circle w-50 border border-info border-3">
                            </div>
                        </div>                        
                    </div>                    
                </div>
            </aside>         
        </div>    

    </div>
    <!-- ### .preview-image-container | END ### -->

    <div class="my-5 border-1 border"></div>

    <!-- ### .preview-image-container | START ### -->
    <div class="container preview-image-container">
        <div class="row">
            <div class="col-md-6">
                <div class="mt-5"></div>
                <h2 class="h5 mb-2 pb-1">Upload file</h2>
                <p class="mb-4 fs-sm">File types supported: PNG, JPEG, JPG. Max size: 1mb.</p>

                <!-- invalid message -->
                <div class="invalid-feedback invalidFileSize" style="display:none;">The maximum file is 1mb. Please reduce the file size or change the new file.</div>
                <div class="invalid-feedback invalidFileType" style="display:none;">Only PNG, JPEG and JPG file type are acceptable. Please choose the new file type.</div>

                <!-- Drag and drop file upload -->
                <div class="file-drop-area">
                    <div class="file-drop-icon"><i class="fa-solid fa-cloud-arrow-up"></i></div>                    
                    <span class="file-drop-message">Drag and drop here to upload</span>
                    <input type="file" class="file-drop-input" accept=".png, .jpg, .jpeg">
                    <button type="button" class="file-drop-btn btn btn-primary btn-sm">Or select file</button>
                    <button type="button" class="btn btn-outline-secondary btn-sm remove-upload-btn">Remove</button>
                </div>      

                <!-- Note -->
                <div class="mt-5 text-muted">
                    <span><b>In this tutorial</b>, we want the users to view the uploaded image on the card design before saving.</span>
                </div>

            </div>

            <!-- preview image section -->
            <aside class="col-md-6 mt-0 text-center">
                <div class="mt-5"></div>

                <!-- preview image card -->
                <div class="card border-0 shadow bg-light d-inline-block" style="width: 400px; height: 600px; pointer-events: none; border-radius: 50px;">
                    <div class="d-flex align-items-end justify-content-start w-100 h-100">  

                        <!-- preview image -->
                        <img src="" alt="" class="position-absolute top-0 preview-image" style="display: none; border-radius: 50px; width: 400px; height: 600px; object-fit: cover;">

                        <!-- extra info -->
                        <div class="d-flex justify-content-between align-items-center mb-3 mx-5 mt-5 position-relative" style="z-index:100;">
                            <div class="d-inline-block w-50">
                                <span class="text-white bg-info bg-gradient rounded-pill p-2 preview-image-title"><i class="fa-solid fa-location-dot"></i>&nbsp;&nbsp;Krabi,Thailand</span> 
                            </div>
                            <div class="d-inline-block text-end w-50">
                                <img src="img/person-dummy.jpg" alt="https://www.freepik.com/drobotdean" class="rounded-circle w-50 border border-info border-3">
                            </div>
                        </div>                        
                    </div>                    
                </div>
            </aside>         
        </div>    

    </div>
    <!-- ### .preview-image-container | END ### -->


    <!-- add our custom js -->
    <script src="file-drag-drop.js"></script>
</body>
</html>

In the code above, it is simply the HTML code. We add Bootstrap5 and font awesome which are the third parties. We also add the stype.css and file-drag-drop.js which we add the code in the next section.

Add the style.css

  • Create the new style.css
  • Add the code below into the style.css
.file-drop-area {
    position: relative;
    padding: 2rem 1rem;
    transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
    border: 2px dashed #dae1e7;
    background-color: #fff;
    text-align: center;
    cursor: pointer;
}
.file-drop-area .file-drop-icon {
    display: block;
    margin-bottom: 0.75rem;
    color: #aeb4be;
    font-size: 1.625rem;
}    
.file-drop-area .file-drop-message {
    display: block;
    font-size: .875rem;
    margin-bottom: 1.25rem;
}    
.file-drop-area .file-drop-input {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: transparent;
    opacity: 0;
    outline: none;
    cursor: pointer;
    z-index: 2;
}   
.file-drop-area .btn {
    position: relative;
    z-index: 3;
}    
.file-drop-area .file-drop-preview {
    max-width: 16rem;
    margin-right: auto;
    margin-bottom: 0.75rem;
    margin-left: auto;
} 
img, figure {
    max-width: 100%;
    height: auto;
    vertical-align: middle;
}      

Add the file-drag-drop.js

  • Create a new file-drag-drop.js
  • Copy the code below and paste it to the file-drag-drop.js
// ## custom drag and drop input field 
var fileDropArea = function () {
    var dropArea = document.querySelectorAll('.file-drop-area');
    // a container of file drop area input field and preview image section
    var previewContainer = document.querySelectorAll('.preview-image-container');

    var chkAllDropArea = function chkAllDropArea(i) {
        let input = dropArea[i].querySelector('.file-drop-input'),
            message = dropArea[i].querySelector('.file-drop-message'),
            icon = dropArea[i].querySelector('.file-drop-icon'),
            selectButton = dropArea[i].querySelector('.file-drop-btn'),
            removeButton = dropArea[i].querySelector('.remove-upload-btn'),
            invalidFileSize = previewContainer[i].querySelector('.invalidFileSize'),
            invalidFileType = previewContainer[i].querySelector('.invalidFileType');

        message.innerHTML = "Drag and drop here to upload";

        selectButton.addEventListener('click', function () {
            input.click();
        });

        removeButton.addEventListener('click', function () {
            input.value = '';
            invalidFileSize.style.display = 'none';
            invalidFileType.style.display = 'none';
            message.innerHTML = "Drag and drop here to upload";

            // input file
            let fileDropPreview = previewContainer[i].querySelector('.file-drop-preview');
            if (fileDropPreview && fileDropPreview.querySelector("img")) {

                // remove the file drop icon
                fileDropPreview.querySelector("img").remove();
                fileDropPreview.className = "file-drop-icon";

                // add the upload icon
                const originIcon = document.createElement("i");
                originIcon.className = "fa-solid fa-cloud-arrow-up";
                icon.appendChild(originIcon);
            }

        });

        input.addEventListener('change', function () {
            // # preview image at the input file
            if (input.files && input.files[0] && ((input.files[0].type == "image/jpeg") || (input.files[0].type == "image/png"))) {
                var reader = new FileReader();

                reader.onload = function (e) {
                    var fileData = e.target.result;
                    var fileName = input.files[0].name;
                    message.innerHTML = fileName;

                    if (fileData.startsWith('data:image')) {
                        var image = new Image();
                        image.src = fileData;

                        image.onload = function () {
                            // CSS is already added in the style script
                            icon.className = 'file-drop-preview img-thumbnail rounded';
                            icon.innerHTML = '<img src="' + image.src + '" alt="' + fileName + '">';
                        };
                        // # Note: You can add the video and document file type using this code below as well.
                        // On the preview section, you must add your own custom code to make it work as you want.
                        // } else if (fileData.startsWith('data:video')) {
                        //     icon.innerHTML = '';
                        //     icon.className = '';
                        //     icon.className = 'file-drop-icon ci-video';  
                        // } else {
                        //     icon.innerHTML = '';
                        //     icon.className = '';
                        //     icon.className = 'file-drop-icon ci-document'; 
                    }
                };

                reader.readAsDataURL(input.files[0]);
            }


            // # input field validation
            if (!input.files) { // This is VERY unlikely, browser support is near-universal
                console.error("This browser doesn't seem to support the `files` property of file inputs.");

            } else if (input.files[0]) {
                let file = input.files[0];

                // Check filesize is over 1 mb(1000000 bytes)
                if (file.size > 1000000) {
                    // show a warning message
                    invalidFileSize.style.display = "block";

                    // Check file type must be only PNG, JPEG and JPG
                } else if (!((file.type == "image/jpeg") || (file.type == "image/png"))) {
                    // show a warning message
                    invalidFileType.style.display = "block";

                } else {
                    // the file is all good

                    // hide a warning message
                    invalidFileSize.style.display = "none";
                    invalidFileType.style.display = "none";
                }
            }
        });
    };

    var previewImage = function previewImage(i) {
        // # preview image
        const uploadInput = previewContainer[i].querySelector('.file-drop-input');
        const previewImageElement = previewContainer[i].querySelector('.preview-image');

        uploadInput.addEventListener('change', function () {
            const [file] = uploadInput.files;

            if (file && ((file.type == "image/jpeg") || (file.type == "image/png"))) {
                previewImageElement.style.display = 'block';
                previewImageElement.src = URL.createObjectURL(file)
            }
        });

        // # remove preview image
        previewContainer[i].querySelector('.remove-upload-btn').addEventListener('click', function () {
            previewImageElement.src = '';
            previewImageElement.style.display = 'none';

        });
    }

    for (var i = 0; i < dropArea.length; i++) {
        chkAllDropArea(i);
        previewImage(i);
    }
}();  

The result after uploading the image

Now your file structure will look like this.

  • index.html
  • file-drag-drop.js
  • style.css

For the dummy person image, you can create the img folder and replace the dummy person image with your own (you will see the image link in the HTML code). I provide the full code and resource at the end of the post so you can download it.

Below is how the file drag and drop field and preview image work from the code.

Result of uploaded image

Explanation

  • Users can select the image file or drag and drop the image file into the input file.
  • The file size should be less than 1 MB and the file type must be jpeg or png only. We will show the invalid message if the uploaded file doesn’t meet the condition.
  • Users can remove the uploaded image by clicking on the remove button. The remove feature is useful on the form. So users won’t update the current input file if they don’t want to.
  • We wrap the input file and preview section with div.preview-image-container. Meaning you can duplicate the whole div.preview-image-container elements in order to create the new input file with the preview section. The JS code still works with the new duplicated container without any changes.

Drop area

For the code above, the fileDropArea function is the drag and drop file controller. The function will search for all input files with the file-drop-area class on the page. After that, it will loop through all found elements and display the thumbnail within the file drop area. It uses the FileReader object in order to read the contents of files. The sample on how to use FileReader can be found here.

Note that, this tutorial will work with the image file type only. You can add the custom code for the video and document file type yourself.

Remove button

When the user clicks on the remove button, we will reset the thumbnail and preview section.

Preview section

We preview the image in the preview section using the card design we want. In my case, I use this card design in many template components (grid component, carousel component, etc) in my project. I want the user to be able to choose the perfect image and fit the card design by themself.

Field validation

We do the field validation as below.

  • check the uploaded file type
  • check the uploaded file size

Download source code

Like my work? You can support me by buying me a coffee.

Creating a drag and drop uploader with preview in Javascript (3821 downloads )

Cartzilla Bootstrap Template

Part of the code in this tutorial is from Cartzilla Bootstrap Template which I have been working with the template for several months. I can recommend anyone who is looking for a good quality template and is ready to use it on any web project. Using Cartzilla Bootstrap Template saves your developing time. The code is clean and easy to add the custom code you want. The template support is fast and helpful. Using my affiliate link in this post will help me push more useful posts too.

Wrap up

Drag and Drop with preview image feature is a must nowadays. I strongly suggest the uploaded image input file implement this feature. It is very helpful for the user. Like my post, please consider buying me a coffee. I will make my day.