【问题标题】:Find if 'cancel' was clicked on file input查找是否在文件输入上单击了“取消”
【发布时间】:2014-05-18 21:29:23
【问题描述】:

我尝试使用在不同位置描述的 hack:

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 步:SelectFile - 选择一些文件(并注意输出)

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

【问题讨论】:

  • 你的意思是document.body.onfocus = checkOnCancel;? (没有函数调用。)
  • @Scimonster - 不,我的意思是 document.body.onfocus = checkOnCancel(); (带函数调用)

标签: javascript html file input


【解决方案1】:

一种解决方案是使用inputonchange 事件。

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 控件的属性完成更新之前。

更新:要确定用户每次关闭文件对话框时选择的内容,即使没有任何更改,也可以通过在再次接收焦点之间添加短暂延迟来避开时间问题,并且检查文件输入的值。 charge 的以下版本不是在收到焦点后立即调用 checkOnCancel,而是使其在十分之一秒后被调用。

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

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

【讨论】:

  • 使用onclick的目的是为了捕捉取消事件和连续两次选择同一个文件的情况。时间问题有什么解决办法吗?即强制更新值?
  • 当焦点回到正文时听听真的是个好主意;)
  • 一个问题,如果你将鼠标悬停在身体​​上,当盒子仍然打开时,警报弹出窗口会弹出,但任何方式似乎都是迄今为止最好的解决方案
  • 如果正文已经是文档的“activeElement”,这将不起作用。但是您可以做的是使用“鼠标悬停”事件并应用相同的逻辑!
  • 此解决方案仅在setTimeOut 函数触发之前由浏览器完全读取所选文件时才有效 - 较大的文件需要更多时间,因此checkOnCancel 函数将在@987654333 时给出不正确的响应@ 开火太早了。您可以轻松地增加超时时间,但这引出了一个问题:多长时间才足够?
【解决方案2】:

您可以挂钩window.focus 事件,当他们取消窗口的文件选择框时会触发该事件。然后检查它是否真的选择了一个文件。

【讨论】:

  • 这是正确的方法。非常适合我。
【解决方案3】:

//此代码在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);
    }

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

    • 即使用户选择文件而不是取消,我也会得到以前的执行值 - jsfiddle.net/smV9c/2
    • 可以通过访问文件列表$(':file')[0].files获取选中的文件
    • 奇怪的“预期行为”。在 linux 桌面上使用 zenity 时,当我取消一个窗口时,我也会得到一个返回值。
    【解决方案5】:

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

    • 在大多数浏览器上,文件选择器会立即打开并将用户带出浏览器。我们可以使用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();
      });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-31
      • 2017-11-09
      • 2018-06-27
      • 1970-01-01
      • 2015-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多