0

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?

Skerdi Velo
  • 121
  • 2
  • 13
Rahul
  • 1,091
  • 1
  • 8
  • 12
  • Does this answer your question? [Is possible to know when file input dialog is closing?](https://stackoverflow.com/questions/47995415/is-possible-to-know-when-file-input-dialog-is-closing) – Madan Bhandari May 07 '23 at 12:36
  • If you explained why this would be useful to a situation in your application, people might be able to offer better suggestions. I personally can think of no reason I would want to do this; opening the file chooser dialog and then closing it has no effect on the state of the page. – Pointy May 07 '23 at 16:07

3 Answers3

1

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!\n\n'
      + [...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.

Thomas Frank
  • 1,404
  • 4
  • 10
  • Thanks for the response! I have adopted the solution it works great but is a little inconsistent sometimes I am building an app with angular PWA and WASM so that i can give a native app like performance so I am putting the feature asside for now I have come across a experimental feature which has all the functions I need, will post it here – Rahul May 11 '23 at 10:01
0

This is someting I found when searching for the solution though it is a experimental feature but would have been a perfect solution for my problem.

<button onclick="upload()">upload</button>
async function upload() {
  try {
    const pickerOpts = {
      types: [
        {
          description: "Images",
          accept: {
            "image/*": [".png", ".gif", ".jpeg", ".jpg"],
          },
        },
      ],
      excludeAcceptAllOption: true,
      multiple: true,
    };
    const fileHandle = await window.showOpenFilePicker(pickerOpts);
    const files = [];
    for (const file of fileHandle) {
      files.push(await file.getFile());
    }
    await doUpload(files); // upload the files
  } catch (err) {
    console.log(err);
    // do something
  }
}

here the function showOpenFilePicker will expect a options object and will also reject with a abort error if the user is rejecting the file input popup.

This would have been a perfect solution with no work arround required but is in experimental stage so...

You can get more info for this function here https://developer.mozilla.org/en-US/docs/Web/API/Window/showOpenFilePicker

Rahul
  • 1,091
  • 1
  • 8
  • 12
-2

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

Rookie
  • 44
  • 5