检测用户关闭文件输入窗口

huangapple go评论66阅读模式
英文:

Detect user closing the file input window

问题

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?

英文:

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

what I am trying:

&lt;input type=&quot;file&quot; /&gt;
      const input = document.querySelector(&quot;input&quot;);
      input.addEventListener(&quot;change&quot;, handleFileUpload);

      async function handleFileUpload(event) {
        if (event.userCancel) {
          //  &lt;--- 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?

答案1

得分: 1

以下是您要翻译的内容:

"I've tested the following solution in the 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 something 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 their 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 a way around it. The code has been updated.

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

如果您需要任何其他帮助,请随时告诉我。

英文:

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):

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const sleep = ms =&gt; new Promise(res =&gt; setTimeout(res, ms));
const input = document.querySelector(&quot;input&quot;);

input.addEventListener(&#39;click&#39;, () =&gt; handleFileUpload());

async function handleFileUpload() {
  input.value = &#39;&#39;;
  let closed = false;
  let ua = navigator.userAgent.toLowerCase().toLowerCase();
  let firefoxOrSafari = ua.includes(&#39;firefox&#39;) || ua.includes(&#39;safari&#39;);
  let eventType =  firefoxOrSafari ? &#39;mouseover&#39; : &#39;focus&#39;;
  let listener = window.addEventListener(eventType, () =&gt; closed = true);
  while (!closed) {
    await sleep(100);
    !firefoxOrSafari &amp;&amp; input.focus();
  }
  window.removeEventListener(eventType, listener);
  await sleep(1000);
  const files = input.files;
  if (!files.length) {
    alert(&#39;No file upload today then?&#39;)
  }
  else {
    alert(&#39;Uploading!\n\n&#39;
      + [...files].map(({ name }) =&gt; name).join(&#39;\n&#39;));
    // await doUpload(files); // upload the files
    input.value = &#39;&#39;;
  }
}

<!-- language: lang-html -->

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;

&lt;head&gt;
  &lt;meta charset=&quot;UTF-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
  &lt;title&gt;Sticky Color&lt;/title&gt;
  &lt;link rel=&quot;stylesheet&quot; href=&quot;sticky-color.css&quot;&gt;
&lt;/head&gt;

&lt;body&gt;
  &lt;input type=&quot;file&quot; multiple&gt;
&lt;/body&gt;

&lt;/html&gt;

<!-- end snippet -->

  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.

答案2

得分: 0

这是我在寻找解决方案时发现的东西,虽然它是一个实验性功能,但对于我的问题来说本应是一个完美的解决方案。

&lt;button onclick=&quot;upload()&quot;&gt;上传&lt;/button&gt;
async function upload() {
  try {
    const pickerOpts = {
      types: [
        {
          description: &quot;图像&quot;,
          accept: {
            &quot;image/*&quot;: [&quot;.png&quot;, &quot;.gif&quot;, &quot;.jpeg&quot;, &quot;.jpg&quot;],
          },
        },
      ],
      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); // 上传文件
  } catch (err) {
    console.log(err);
    // 做一些处理
  }
}

这里的函数 showOpenFilePicker 将期望一个选项对象,并且如果用户拒绝文件输入弹窗,它也会拒绝并返回中止错误。

这本应是一个完美的解决方案,不需要任何解决办法,但它仍处于实验阶段,所以...

你可以在这里获取更多关于这个函数的信息:https://developer.mozilla.org/en-US/docs/Web/API/Window/showOpenFilePicker

英文:

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.

&lt;button onclick=&quot;upload()&quot;&gt;upload&lt;/button&gt;
async function upload() {
  try {
    const pickerOpts = {
      types: [
        {
          description: &quot;Images&quot;,
          accept: {
            &quot;image/*&quot;: [&quot;.png&quot;, &quot;.gif&quot;, &quot;.jpeg&quot;, &quot;.jpg&quot;],
          },
        },
      ],
      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

答案3

得分: -2

我是你的中文翻译:

我是说,当输入更改时,函数upload()会触发,没有时间取消任何事情。

如果他们可以的话,一定有一件可以触发取消事件的事情,也许是一个取消按钮,并且提供一个提交按钮或倒计时来自动提交。

英文:

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

huangapple
  • 本文由 发表于 2023年5月7日 20:15:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76193866.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定