skip to Main Content

I am facing an issue in detecting a file input window close event in js

what I am trying:

<input type="file" />
      const input = document.querySelector("input");
      input.addEventListener("change", handleFileUpload);

      async function handleFileUpload(event) {
        if (event.userCancel) {
          //  <--- where can I get this?? or something similar in function
          // do something
        }

        const files = event.target.files;
        await doUpload(files); // upload the files
      }

Any advice on how to fix this?

2

Answers


  1. I mean, the function upload() triggers when input changes, there is no time to cancel anything.

    if they can, there must have a thing they can trigger the cancel event, maybe a cancel button, and also provide a submit button or countdown for auto submit

    Login or Signup to reply.
  2. I’ve tested the following solution in latest Chrome, Firefox and Safari on my Mac (May, 2023) and it seems to work fine (an in-depth explanation follows below the code):

    const sleep = ms => new Promise(res => setTimeout(res, ms));
    const input = document.querySelector("input");
    
    input.addEventListener('click', () => handleFileUpload());
    
    async function handleFileUpload() {
      input.value = '';
      let closed = false;
      let ua = navigator.userAgent.toLowerCase().toLowerCase();
      let firefoxOrSafari = ua.includes('firefox') || ua.includes('safari');
      let eventType =  firefoxOrSafari ? 'mouseover' : 'focus';
      let listener = window.addEventListener(eventType, () => closed = true);
      while (!closed) {
        await sleep(100);
        !firefoxOrSafari && input.focus();
      }
      window.removeEventListener(eventType, listener);
      await sleep(1000);
      const files = input.files;
      if (!files.length) {
        alert('No file upload today then?')
      }
      else {
        alert('Uploading!nn'
          + [...files].map(({ name }) => name).join('n'));
        // await doUpload(files); // upload the files
        input.value = '';
      }
    }
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Sticky Color</title>
      <link rel="stylesheet" href="sticky-color.css">
    </head>
    
    <body>
      <input type="file" multiple>
    </body>
    
    </html>
    1. The idea is that we listen to click instead of change. This calls the listener before the file dialog opens.
    2. We reset the input to no files (value = ”)
    3. In the listener function we attach a new listener to mouseover – this doesn’t trigger while the dialog is open, since the viewport is ‘muted’.
    4. While the mouseover event does not trigger we wait (async sleep).
    5. When the mouseover event triggers we assume that the dialog is closed, and remove the mouseover listener.
    6. We also wait a short while after this (1 second) so that the browser has time to update the input value. Seems to be needed mostly in Chrome.
    7. If we then have zero files the user has canceled.
    8. Else we have somthing to upload. (I don’t actually upload anything in this example.)
    9. After upload we can reset the input to no files again if we want.

    Note: This solution is sensitive to the user moving they mouse outside the viewport during the time the file dialog is open. This is rather unlikely though. And the ‘close’ triggers once they move the mouse back inside the viewport. (For us developers it is more likely to move outside the viewport since we might have the console/dev tools open. And when running the code snippet here it runs in a small iframe, so it is rather likely that you move outside that iframe.)

    Update: Unfortunately the mouseover event sometimes triggers on Chromium/Blink even when the dialog is open, so I opted for keeping the original solution in Firefox and Safari and going with another one for other browsers (~ probably Chrome-based): Listen to focus of the input field, and while we wait try to focus the input field – in Chrome this doesn’t work as long as the dialog is open… I don’t like having to version things per browser, but can’t find away around it. The code has been updated.

    And DO note: Not tested on or adopted for mobile devices.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search