查找文件输入中是否单击了“取消”

问题描述 投票:0回答:6

我尝试使用在不同位置描述的黑客,它使用:

document.body.onfocus = checkOnCancel();

示例:

var fileSelectEle = document.getElementById('fileinput');

fileSelectEle.onclick = charge;

function charge()
{
    document.body.onfocus = checkOnCancel;
}

function checkOnCancel()
{
    alert("FileName:" + fileSelectEle.value + "; Length: " + fileSelectEle.value.length);
    if(fileSelectEle.value.length == 0) alert('You clicked cancel!')
    else alert('You selected a file!');
    document.body.onfocus = null;
}

这里有什么问题吗?因为

fileSelectedEle.value
始终返回上一个执行值,而不是用户选择的值。 这是输入文件的预期行为吗?如何解决此问题以读取所选的实际文件?

http://jsfiddle.net/smV9c/2/

您可以通过以下方式重现错误:

第 1 步:选择文件 - 一些选择一些文件(并注意输出)

第 2 步:选择文件 - 按取消(并注意输出)

javascript html file input
6个回答
17
投票

一种解决方案是使用

onchange
input
事件。

var fileSelectEle = document.getElementById('fileinput');

fileSelectEle.onchange = function ()
{
  if(fileSelectEle.value.length == 0) {
    alert('You clicked cancel - ' + "FileName:" + fileSelectEle.value + "; Length: " + fileSelectEle.value.length);
  } else {
    alert('You selected a file - ' + "FileName:" + fileSelectEle.value + "; Length: " + fileSelectEle.value.length);
  }
}

这可以正确响应所选文件名的更改,您可以在此处进行测试:http://jsfiddle.net/munderwood/6h2r7/1/

行为与您尝试执行此操作的方式的唯一潜在差异是,如果您立即取消,或连续两次取消,或连续两次选择同一文件,则该事件将不会触发。但是,每次文件名实际更改时,您都会正确检测到它。

我不确定为什么您最初的尝试不起作用,尽管我最好的猜测是这是

onfocus
事件异步触发的计时问题,并且在
input
控件的属性完成更新之前。

更新:要确定用户每次关闭文件对话框时选择的内容,即使没有任何更改,也可以通过在再次接收焦点和检查文件输入的值之间添加短暂的延迟来避免计时问题。以下版本的

checkOnCancel
不是在收到焦点后立即调用
charge
,而是在十分之一秒后调用它。

function charge() {
  document.body.onfocus = function () { setTimeout(checkOnCancel, 100); };
}

这是一个工作版本:http://jsfiddle.net/munderwood/6h2r7/2/


3
投票

您可以挂钩

window.focus
事件,当取消窗口的文件选择框时会触发该事件。然后检查它是否确实选择了文件。


1
投票

//此代码适用于 chrome 的文件选择尝试一下

     <--write this line in HTML code-->
     <input type='file' id='theFile' onclick="initialize()" />  
    var theFile = document.getElementById('theFile');
    function initialize() {
        document.body.onfocus = checkIt;
        console.log('initializing');
    }

    function checkIt() {
        setTimeout(function() {
            theFile = document.getElementById('theFile');
            if (theFile.value.length) {
                alert('Files Loaded');
            } else {
                alert('Cancel clicked');
            }
            document.body.onfocus = null;
            console.log('checked');
        }, 500);
    }

1
投票

处理用户取消文件输入的所有各种方式是很棘手的。

  • 在大多数浏览器上,文件选择器会立即打开并将用户带出浏览器。我们可以使用
    window.focus
    事件来检测他们何时回来,而无需选择任何内容来检测取消
  • 在 iOS 浏览器上,用户首先看到一个 iOS 模式,可以让他们在相机和图库之间进行选择。用户可以通过单击远离模式来从此处取消。所以,我们可以使用
    window.touchend
    来检测这个
  • 可能还有其他浏览器和案例在取消时表现不同,这也尚未捕获

在实现方面,您可以使用

addEventListener
确保不会替换窗口上可能已经存在的其他事件侦听器 - 并在事件侦听器触发后轻松清理它。例如:

window.addEventListener('focus', () => console.log('no file selected'), { once: true });

这是一个示例,说明如何使用它以编程方式获取图像,处理上面列出的注意事项(打字稿):

/**
 * opens the user OS's native file picker, returning the selected images. gracefully handles cancellation
 */
export const getImageFilesFromUser = async ({ multiple = true }: { multiple?: boolean } = {}) =>
  new Promise<File[]>((resolve) => {
    // define the input element that we'll use to trigger the input ui
    const fileInput = document.createElement('input');
    fileInput.setAttribute('style', 'visibility: hidden'); // make the input invisible
    let inputIsAttached = false;
    const addInputToDom = () => {
      document.body.appendChild(fileInput); // required for IOS to actually fire the onchange event; https://stackoverflow.com/questions/47664777/javascript-file-input-onchange-not-working-ios-safari-only
      inputIsAttached = true;
    };
    const removeInputFromDom = () => {
      if (inputIsAttached) document.body.removeChild(fileInput);
      inputIsAttached = false;
    };

    // define what type of files we want the user to pick
    fileInput.type = 'file';
    fileInput.multiple = multiple;
    fileInput.accept = 'image/*';

    // add our event listeners to handle selection and canceling
    const onCancelListener = async () => {
      await sleep(50); // wait a beat, so that if onchange is firing simultaneously, it takes precedent
      resolve([]);
      removeInputFromDom();
    };
    fileInput.onchange = (event: any) => {
      window.removeEventListener('focus', onCancelListener); // remove the event listener since we dont need it anymore, to cleanup resources
      window.removeEventListener('touchend', onCancelListener); // remove the event listener since we dont need it anymore, to cleanup resources
      resolve([...(event.target!.files as FileList)]); // and resolve the files that the user picked
      removeInputFromDom();
    };
    window.addEventListener('focus', onCancelListener, { once: true }); // detect when the window is refocused without file being selected first, which is a sign that user canceled (e.g., user left window into the file system's file picker)
    window.addEventListener('touchend', onCancelListener, { once: true }); // detect when the window is touched without a file being selected, which is a sign that user canceled (e.g., user did not leave window - but instead canceled the modal that lets you choose where to get photo from on ios)

    // and trigger the file selection ui
    addInputToDom();
    fileInput.click();
  });

0
投票

这里有什么问题吗?因为 fileSelectedEle.value 始终返回上一个执行值,而不是用户选择的值。这是输入文件的预期行为吗?如何解决此问题以读取所选的实际文件?

没有什么问题,这是预期的行为。如果用户取消文件选择过程,就好像他们从未启动过该过程一样。所以之前的值保留在原处。


0
投票

有一个文件输入取消事件可以使用。

HTML

<label for="file">Select or file. Or don't.</label>
<input type="file" id="file" name="file" />

<div id="result"></div>

Javascript

const elem = document.getElementById("file");

const result = document.getElementById("result");

elem.addEventListener("cancel", () => {
 result.textContent = "Canceled.";
 });`

更多详细信息请参见文档

© www.soinside.com 2019 - 2024. All rights reserved.