How to Drag and Drop to Reorder Scanned Document Pages in JavaScript
Drag and drop is a useful feature for managing scanned document images. It allows users to move items from one location to another using a mouse or touch gesture.
Dynamsoft Document Viewer is an SDK which provides a set of viewers for document images. In this article, we are going to demonstrate how to use it to display images and use drag and drop to reorder images. In addition, we will also explore how to implement it from scratch with JavaScript using the drag and drop API.
What you’ll build: A JavaScript web page that loads scanned document images and lets users drag and drop to reorder pages — using Dynamsoft Document Viewer’s Browse Viewer or a custom vanilla JavaScript implementation.
Key Takeaways
- Dynamsoft Document Viewer’s Browse Viewer provides built-in drag-and-drop page reordering with no extra code required.
- The HTML5 Drag and Drop API lets you implement custom reordering logic for image thumbnails in vanilla JavaScript.
- A touch-device polyfill (
drag-drop-touch) is needed because the native Drag and Drop API does not fire on mobile browsers. - This approach works for scanned documents, multi-page PDFs, and any image set that needs manual page ordering.
Common Developer Questions
- How do I drag and drop to reorder scanned document pages in JavaScript?
- Does the HTML5 Drag and Drop API work on mobile and touch devices?
- How do I use Dynamsoft Document Viewer to reorder document images in the browser?
Reorder Document Pages with Dynamsoft Document Viewer
-
Create a new HTML file with the following template.
<!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, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Drag and Drop</title> <style> </style> </head> <body> </body> <script> </script> </html> -
Include Dynamsoft Document Viewer’s files.
<script src="https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@2.0.0/dist/ddv.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@2.0.0/dist/ddv.css"> -
Initialize Dynamsoft Document Viewer with a license. You can apply for one here.
Dynamsoft.DDV.Core.license = "LICENSE-KEY"; Dynamsoft.DDV.Core.engineResourcePath = "https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@2.0.0/dist/engine";// Lead to a folder containing the distributed WASM files await Dynamsoft.DDV.Core.init(); -
Create a new document instance.
const docManager = Dynamsoft.DDV.documentManager; const doc = docManager.createDocument(); -
Create an instance of browse viewer, bind it to a container and use it to view the document we just created.
HTML:
<div id="viewer"></div>JavaScript:
let browseViewer = new Dynamsoft.DDV.BrowseViewer({ container: document.getElementById("viewer"), }); browseViewer.openDocument(doc.uid);CSS:
#viewer { width: 320px; height: 480px; } -
Use
inputto select image or PDF files and load them into the document instance so that we can use the browse viewer to view the files.HTML:
<label> Select an image: <br/> <input type="file" id="files" name="files" onchange="filesSelected()"/> </label>JavaScript:
async function filesSelected(){ let filesInput = document.getElementById("files"); let files = filesInput.files; if (files.length>0) { const file = files[0]; const blob = await readFileAsBlob(file); await doc.loadSource(blob); // load image } } function readFileAsBlob(file){ return new Promise((resolve, reject) => { const fileReader = new FileReader(); fileReader.onload = async function(e){ const response = await fetch(e.target.result); const blob = await response.blob(); resolve(blob); }; fileReader.onerror = function () { reject('oops, something went wrong.'); }; fileReader.readAsDataURL(file); }) }
We can then drap and drop to reorder the images in the viewer.
Build Drag-and-Drop Page Reordering with Vanilla JavaScript
HTML5 has the Drag and Drop API out of the box.
In the previous article, we’ve written a page to view and select multiple images.
We are now going to add the drap and drop feature to it. It works by dragging the selected images into another image to adjust the image order.
-
Set the draggable attribute to true for the selected image elements.
function toggleSelection(thumbnail) { thumbnail.classList.toggle('selected'); + if (thumbnail.classList.contains('selected')) { + thumbnail.getElementsByTagName("img")[0].setAttribute("draggable",true); + }else{ + thumbnail.getElementsByTagName("img")[0].setAttribute("draggable",false); + } } function selectOne(thumbnail) { thumbnail.classList.add('selected'); + thumbnail.getElementsByTagName("img")[0].setAttribute("draggable",true); } function clearSelection() { let thumbnails = document.querySelectorAll(".thumbnail"); - thumbnails.forEach(thumbnail => thumbnail.classList.remove('selected')); + thumbnails.forEach(thumbnail => { + thumbnail.classList.remove('selected') + thumbnail.getElementsByTagName("img")[0].setAttribute("draggable",false); + }); + } -
Add
dragenteranddragleaveevents to add effects to the image dragged into. Meanwhile, keep track of the image dragged into astargetImg.let targetImg; img.addEventListener("dragenter",function(){ img.classList.add("dragHover"); targetImg = img; }) img.addEventListener("dragleave",function(){ img.classList.remove("dragHover"); })CSS:
.dragHover { opacity: 0.5; } -
Add the
dragoverevent to set the drop effect.img.addEventListener("dragover",function(e){ e.dataTransfer.dropEffect = "move"; }) -
Add the
dragendevent to remove the dragging effect and perform reordering. First, we get all the selected images, the first selected image’s index and the target image’s index. Then, if the target index is bigger than the selected index, useinsertBeforeto insert the images before the next image of the target image. Otherwize, insert the images right before the target image.img.addEventListener("dragend",function(){ img.classList.remove("dragHover"); if (img.classList.contains("selected") === false) { reorder(); } }) function reorder(){ const targetThumbnailContainer = targetImg.parentNode; targetImg.classList.remove("dragHover"); const thumbnails = document.getElementsByClassName("thumbnail"); const selected = []; let targetIndex = -1; let selectedIndex = -1; for (let index = thumbnails.length - 1; index >=0 ; index--) { const item = thumbnails[index]; if (item.classList.contains("selected")) { selected.push(item); selectedIndex = index; if (item === targetThumbnailContainer) { //abort if the target image is also selected return; } } if (item === targetThumbnailContainer) { targetIndex = index; } } const viewer = document.getElementById("viewer"); if (selectedIndex>targetIndex) { selected.reverse(); } selected.forEach(function(one) { if (selectedIndex<targetIndex) { viewer.insertBefore(one,targetThumbnailContainer.nextSibling); }else{ viewer.insertBefore(one,targetThumbnailContainer); } }) } -
The drag and drop API does not work on touch devices. We can enable it using a polyfill by including the following line in our page.
<script src="https://drag-drop-touch-js.github.io/dragdroptouch/dist/drag-drop-touch.esm.min.js?autoload" type="module"></script>
All right, we’ve implemented the drag and drop feature with JavaScript.
Common Issues and Edge Cases
- Drag and drop not working on mobile/touch devices. The native HTML5 Drag and Drop API does not fire touch events. Include the
drag-drop-touchpolyfill shown in Step 5 above, or switch to pointer events (pointerdown,pointermove,pointerup) for a framework-free approach. - Dropped image lands in the wrong position. This typically happens when the
dragentertarget is a child element (e.g., an<img>inside a wrapper<div>). Always resolve the drop target to the thumbnail container usingparentNodeorclosest()before calculating indices. - Reorder state lost after page reload. The DOM-based reorder shown here is in-memory only. Persist the new page order by saving the index array to
localStorageor by callingdoc.movePages()on the Dynamsoft Document Viewer document instance.
Source Code
https://github.com/tony-xlh/document-viewer-samples/tree/main/drag-and-drop