Identifying the conclusion of a folder being dropped in vanilla JavaScript

I am working on determining when a directory tree has been completely traversed after being dropped onto a page. My goal is to identify the point at which the next process can begin once the entire folder and its sub-directories have been processed by the drop event code.

For instance, in the following code snippet, I aim to send the files via JSON to a server after the folder has been processed. Please note that the Ajax call is not included here.

Here is the recursive directory tree traversal function:

async function processEnt(ent,n,fls){
    n = n==undefined? 0:n;
    
 
    if( ent.isFile ){
        fls.files.push(ent.name)        
    }    
    else if (ent.isDirectory){
        
        fls.dirs[ent.name]={ "files":[], "dirs":{},"done":false }                
        var r = ent.createReader()        
        
        await r.readEntries(
            async function f(ents){                                                
                if(ents.length >0){                    
                    for(var e of ents){
                       await processEnt(e,n+1,fls.dirs[ent.name])                    
                    }                                 
                    await r.readEntries(f)
                }
                
            }
        )
        
    }    
    console.log(n +" level done")    
        
}


   

The drop event handler:

async function onDrop(evt){
    evt.preventDefault()
    evt.stopPropagation()
    
    M.files = {
        "files":[],
        "dirs":{}
    }    
    for(item of evt.dataTransfer.items){                
        await processEnt( item.webkitGetAsEntry(),0,M.files )    
    }
    
    console.log("on drop done")
    
}

Answer №1

I have devised a potential solution.

The variable M.paths keeps track of the number of active paths and is incremented for each sub-directory beyond the first one. Additionally, there have been some slight modifications to how files and directories are handled. A timer is utilized to check the value of M.paths to determine if all paths have been completed.

A potential issue that may arise is a race condition where the first sub-directory is processed and M.paths is decremented before any other directories on that level are processed.

Regarding the drop event:

function onDrop(evt){
    evt.preventDefault()
    evt.stopPropagation()
   
    M.paths = 0 
    M.files = {
        "files":[],
        "dirs":[]
    }    
    
    for(item of evt.dataTransfer.items){                
        ent = item.webkitGetAsEntry()
        if(ent.isFile){
           M.files["files"].push(ent) 
        }else if(ent.isDirectory) {
            M.paths+=1
            processDirectory(ent,0,M.files )                
        }
    }
    
    window.setTimeout(function f(){
            if(M.paths==0){
                // drop envent processed 
                //run update or send info to server
                console.log("Drop event done")                    
                
            }else{
                window.setTimeout(f,500)
            }
        },500)
} 

Function for processing each directory:

function processDirectory(ent,n,fls){
    n = n==undefined? 0:n;
        
    var new_dir ={"ent":ent,"files":[],"dirs":[]}
    fls["dirs"].push(new_dir)
    var r = ent.createReader()        
    r.readEntries(
        function f(ents){                                                                
            var leaf = true
            var add_path =0
            for(let entc of ents){
                if ( entc.isDirectory ){
                    M.paths+= add_path
                    add_path = 1
                    console.log(n+":"+M.paths+":"+entc.name)
                    leaf = false
                    processDirectory(entc,n+1,new_dir)
                    
                }else if( entc.isFile){
                    new_dir["files"].push(ent)
                }     
            }
            if(leaf){ M.paths--}                
        }            
    )   
}

This implementation appears to be functional, although it has not undergone extensive testing. It should be noted that when dealing with numerous entries, not all of them may be retrieved. Refer to for more information.

Answer №2

I discovered a solution that appears to work effectively.

Here is the drop event function:

function onDrop (evt){
    
    evt.preventDefault()
    evt.stopPropagation()
    
    var promises = []
    
    for(item of evt.dataTransfer.items){
        //create new Promise that calls processItem function
        // the promise is used to track if the work is complete
        var p  = new Promise( function(res, rej){
        
                processItem(item.webkitGetAsEntry(),res,rej)
            })
            
        // record each promise
        promises.push( p )
        
    }
    
    // the drop event is complete when all of the item (child)
    // promises are completed
    Promise.all( promises ).then(function(){
        // code for after the drop event
    })
    
}

The function responsible for processing each item: function processItem (item,res,rej){

    if (ent.isFile){
        //handle file entry
        
        //complete this items promise
        res()
    }
    else if (ent.isDirectory){
    
        var promises = []
        var dirReader = ent.createReader()
        
        
        function read( entries ){
        
            if (entries.length > 0 ){                
            
                for(var e of entries){
                    var p = new Promise( function(resm,rejm) {                    
                        processItem(e,resm,rejm)
                        })
                    promises.push(p)
                }
                /*
                "readEntries" called again to ensure all entries are read as 
                "readEntries" does not read all files when there 
                is a large amount of files
                */
                dirReader.readEntries(read)
            }
        
        }
        else{
            /*
              if there are no more entries ensure all entries 
              in the directory are processed
             */
             Prmoise.all( promises ).then(function(){
                // once all promises are complete complete this items promise
                res()
            })
            
        }
        // start the read process
        dirReader.readEntries(read)
    }        


}

In testing, all processes were successfully completed before their parent processes.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue with data entry: unable to enter the letter 'S' in search field

This bug has been a real challenge for me. It's strange, but I'm unable to type the letter "S" into the search input field. Oddly enough, the keyboard seems to be functioning properly. Please find the sandbox below for reference: https://codes ...

Form in HTML with Automatic Multiplication Functionality in pure JavaScript

I'm struggling with integrating a simplified HTML form with JavaScript to dynamically adjust and multiply the entered amount by 100 before sending it via the GET method to a specific endpoint. Below is the HTML form: <body> <form method= ...

Tips for locating a file using javascript

My application scans a folder and displays all folders and HTML files inside it in a dropdown menu. It also shows any HTML files inside an iframe. There is one file named "highlighted.html" that should not appear in the dropdown menu, but if it exists in t ...

Exploring Nashorn's Global Object Variables Through Access and Intercept Techniques

I recently came across a question called "Capturing Nashorn's Global Variables" that got me thinking. I'm facing limitations when it comes to capturing the assignment of variables to the global object. For example, let's say I evaluate the ...

Is the treatment of __proto__ different in the fetch API compared to manual assignment?

When using fetch to retrieve a payload containing a __proto__, it seems that the Object prototype is not affected in the same way as when directly assigning to an object. This behavior is beneficial as it ensures that the Object prototype remains unaffect ...

Is handlebars.js giving you trouble all of a sudden?

My handlebars.js template was working perfectly for a week, but suddenly stopped. I'm puzzled by this issue and would appreciate any insights on why it might have stopped functioning. Here is the template: <script id="banners-template" type=" ...

Steps to resolve the error message 'Argument of type 'number' is not assignable to parameter of type 'string | RegExp':

Is there a way to prevent users from using special symbols or having blank spaces without any characters in my form? I encountered an error when trying to implement this in my FormGroup Validator, which displayed the message 'Argument of type 'nu ...

What Causes the "Do Not Push Route with Duplicated Key" Error in React Native with Redux?

I have successfully integrated Redux into my React Native project, specifically for navigation purposes. Below is the code snippet from my navigation reducer file (navReducer.js): import { PUSH_ROUTE, POP_ROUTE } from '../Constants/ActionTypes' ...

The printing feature in javascript does not include the ability to print values from textboxes

I'm encountering an issue when trying to print an HTML table with textboxes that should get their values from another function. However, the preview is not showing anything. Below is the complete code: <html> <head></head> < ...

What is the process for making an ajax call in CakePHP?

It may seem obvious, but I've searched the internet and couldn't find any useful or updated information on this topic. I'm attempting to make an ajax request in CakePHP controller to retrieve both view contents and JavaScript code. The aja ...

I am currently developing a program that is displaying a blank page despite my efforts to write

I've been trying to write the code below but it only shows a blank page. var greeting = React.createClass({ render: function() { return ( <div> <h1>Hello</h1> </div> ...

Generating a JavaScript bundle using the npm TypeScript compiler

Is there a way to compile TypeScript source files into one JavaScript bundle file? We have developed a tool on node.js and are currently using the following TypeScript compilation code: var ts = require('typescript'); ... var program = ts.creat ...

Ways to enlarge the parent container and reveal a concealed child element upon hovering without impacting neighboring containers

Currently, I am diligently working on the company service boxes and have a particular need... When hovering over: 1. The parent div should scale up and acquire box shadow without impacting the neighboring service boxes (it should appear to stand out above ...

Cannot retrieve JSON values in Filestack v3

I am currently working with Filestack (Filepicker) V3 to upload multiple files and retrieve filename, url, and mimetype. Based on the Filestack documentation for this latest release and a previous question that was asked here, I have implemented the follow ...

Guide on grabbing characters/words typed next to # or @ within a div element

Within a div element, I have enabled the contenteditable property. My goal is to capture any text input by the user after typing '#' or '@' until the spacebar key is pressed. This functionality will allow me to fetch suggestions from a ...

The function you are trying to call in Node.js is not defined

Just starting out with NodeJs and trying to implement async/ series, but encountering an error: task.save is not a function Here is the code snippet I am working with: async.series([ (cb) => { Task .findById(id) ...

Is it possible for me to retrieve the error message from my response?

Currently, I am working on displaying backend error messages on the frontend. My approach involves sending a response with an error code and message, then setting a state in my React component to display the error message. While I can successfully display ...

What is the best way to incorporate a state variable into a GraphQL query using Apollo?

My current challenge involves using a state as a variable for my query in order to fetch data from graphQl. However, I'm encountering an issue where the component does not read the state correctly. Any suggestions on how to solve this? class usersSc ...

Locate all elements by a segment of the identification attribute

Is it feasible to achieve the following: I possess a collection of divs, all having IDs that conclude with '_font', such as 'body_font', 'heading_font', 'tagline_font', and so on. Is there a method to retrieve thes ...

Is it possible to compile a .ts file at the root level without following the tsconfig.json configurations?

After dealing with the challenge of having both .ts and .js files coexisting in each folder for months, I have finally managed to get the app to compile correctly. The transition from JS to TS brought about this inconvenience, but the overall benefits make ...