如何检测何时单击文件输入的取消?

Posted

技术标签:

【中文标题】如何检测何时单击文件输入的取消?【英文标题】:How to detect when cancel is clicked on file input? 【发布时间】:2011-06-05 10:33:02 【问题描述】:

如何检测用户何时使用 html 文件输入取消文件输入?

onChange 让我检测他们何时选择文件,但我也想知道他们何时取消(关闭文件选择对话框而不选择任何内容)。

【问题讨论】:

您也可以检测是否有人选择了文件,然后尝试选择发送但取消,您将得到空e.target.files 请你接受一个答案.... 【参考方案1】:
    enter code here
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <h1>Hello</h1>
    <div id="cancel01">
      <button>Cancel</button>
    </div>

    <div id="cancel02">
      <button>Cancel</button>
    </div>

    <div id="cancel03">
      <button>Cancel</button>
    </div>

    <form>
      <input type="file" name="name" placeholder="Name" />
    </form>

    <script>
      const nameInput = document.querySelector('input[type="file"]');

      /******
       *The below code if for How to detect when cancel is clicked on file input
       ******/
      nameInput.addEventListener('keydown', e => 
        /******
         *If the cancel button is clicked,then you should change the input file value to empty
         ******/

        if (e.key == 'Backspace' || e.code == 'Backspace' || e.keyCode == 8) 
          console.log(e);

          /******
           *The below code will delete the file path
           ******/
          nameInput.value = '';
        
      );
    </script>
  </body>
</html>

【讨论】:

仅代码的答案被认为是低质量的:请务必说明您的代码的作用以及它如何解决问题。如果您可以在帖子中添加更多信息,它将帮助提问者和未来的读者。见Explaining entirely code-based answers【参考方案2】:

很多人一直建议更改事件...即使 OP 指定这在问题中不起作用:

单击取消不会选择文件,因此不会触发对文件输入的更改!!!

大多数人建议的所有代码都不会在单击取消时运行。

根据实际阅读 OP 问题的人的建议进行了大量实验后,我提出了这个类来包装文件输入的功能并添加了两个自定义事件:

选择:用户选择了一个文件(如果他们选择了仍然触发 同一个文件)。 cancel:用户已单击取消或以其他方式关闭文件 没有选择的对话框。

我还添加了冗余(监听多个事件以尝试确定是否按下了取消)。可能不会总是立即响应,但至少应该保证在用户重新访问页面时注册取消事件。

最后我注意到事件并不总是保证以相同的顺序发生(尤其是当对话框关闭几乎同时触发它们时)。此类在检查成功标志之前等待 100 毫秒以确保更改事件已触发。

使用 ES6 类,所以在此之前可能无法用于任何东西,但如果你想浪费时间让它在 IE 上运行,你可以编辑它?。

班级:

class FileManager 
    // Keep important properties from being overwritten
    constructor() 
        Object.defineProperties(this, 
            // The file input element (hidden)
            _fileInput: 
                value: document.createElement('input'),
                writeable: false,
                enumerable: false,
                configurable: false
            ,
            // Flag to denote if a file was chosen
            _chooseSuccess: 
                value: false,
                writable: true,
            ,
            // Keeps events from mult-firing
            // Don't want to consume just incase!
            _eventFiredOnce: 
                value: false,
                writable: true,
            ,
            // Called BEFORE dialog is shown
            _chooseStart_handler: 
                value: (event) => 
                    // Choose might happen, assume it won't
                    this._chooseSuccess = false;

                    // Allow a single fire
                    this._eventFiredOnce = false;

                    // Reset value so repeat files also trigger a change/choose
                    this._fileInput.value = '';



                    /* File chooser is semi-modal and will stall events while it's opened */
                    /* Beware, some code can still run while the dialog is opened! */

                    // Window will usually focus on dialog close
                    // If it works this is best becuase the event will trigger as soon as the dialog is closed
                    // Even the user has moved the dialog off of the browser window is should still refocus
                    window.addEventListener('focus', this._chooseEnd_handler);

                    // This will always fire when the mouse first enters the body
                    // A good redundancy but will not fire immeditely if the cance button is not...
                    // in window when clicked
                    document.body.addEventListener('mouseenter', this._chooseEnd_handler);

                    // Again almost a guarantee that this will fire but it will not do so...
                    // imediately if the dialog is out of window!
                    window.addEventListener('mousemove', this._chooseEnd_handler);
                ,
                writeable: false,
                enumerable: false,
                configurable: false
            ,
            _chooseEnd_handler: 
                // Focus event may beat change event
                // Wait 1/10th of a second to make sure change registers!
                value: (event) => 
                    // queue one event to fire
                    if (this._eventFiredOnce)
                        return;

                    // Mark event as fired once
                    this._eventFiredOnce = true;
                    // double call prevents 'this' context swap, IHT!
                    setTimeout((event) => 
                        this._timeout_handler(event);
                    , 100);
                ,
                writeable: false,
                enumerable: false,
                configurable: false
            ,
            _choose_handler: 
                value: (event) => 
                    // A file was chosen by the user
                    // Set flag
                    this._chooseSuccess = true;
                    // End the choose
                    this._chooseEnd_handler(event);
                ,
                writeable: false,
                enumerable: false,
                configurable: false
            ,
            _timeout_handler: 
                value: (event) => 
                    if (!this._chooseSuccess) 
                        // Choose process done, no file selected
                        // Fire cancel event on input
                        this._fileInput.dispatchEvent(new Event('cancel'));
                     else 
                        // Choose process done, file was selected
                        // Fire chosen event on input
                        this._fileInput.dispatchEvent(new Event('choose'));
                    

                    // remove listeners or cancel will keep firing
                    window.removeEventListener('focus', this._chooseEnd_handler);
                    document.body.removeEventListener('mouseenter', this._chooseEnd_handler);
                    window.removeEventListener('mousemove', this._chooseEnd_handler);
                ,
                writeable: false,
                enumerable: false,
                configurable: false
            ,
            addEventListener: 
                value: (type, handle) => 
                    this._fileInput.addEventListener(type, handle);
                ,
                writeable: false,
                enumerable: false,
                configurable: false
            ,
            removeEventListener: 
                value: (type, handle) => 
                    this._fileInput.removeEventListener(type, handle);
                ,
                writeable: false,
                enumerable: false,
                configurable: false
            ,
            // Note: Shadow clicks must be called from a user input event stack!
            openFile: 
                value: () => 
                    // Trigger custom pre-click event
                    this._chooseStart_handler();

                    // Show file dialog
                    this._fileInput.click();
                    // ^^^ Code will still run after this part (non halting)
                    // Events will not trigger though until the dialog is closed
                
            
        );
        this._fileInput.type = 'file';
        this._fileInput.addEventListener('change', this._choose_handler);
    

    // Get all files
    get files() 
        return this._input.files;
    

    // Get input element (reccomended to keep hidden);
    get domElement()
        return this._fileInput;
    

    // Get specific file
    getFile(index) 
        return index === undefined ? this._fileInput.files[0] : this._fileInput.files[index];
    

    // Set multi-select
    set multiSelect(value) 
        let val = value ? 'multiple' : '';
        this._fileInput.setAttribute('multiple', val);
    

    // Get multi-select
    get multiSelect() 
        return this._fileInput.multiple === 'multiple' ? true : false;
    

用法示例:

// Instantiate
let fm = new FileManager();

// Bind to something that triggers a user input event (buttons are good)
let btn = document.getElementById('btn');

// Call openFile on intance to show the dialog to the user
btn.addEventListener('click', (event) => 
  fm.openFile();
);

// Fires if the user selects a file and clicks the 'okay' button
fm.addEventListener('choose', (event) => 
  console.log('file chosen: ' + fm.getFile(0).name);
);

// Fires if the user clicks 'cancel' or closes the file dialog
fm.addEventListener('cancel', (event) => 
  console.log('File choose has been canceled!');
);

可能为时已晚,但我认为这是一个不错的解决方案,涵盖了大多数严重的边缘情况。我将自己使用这个解决方案,所以在我使用它并进一步完善它之后,我最终可能会带着一个 git repo 回来。

【讨论】:

试过这个类,它每次在 Chrome 中触发取消事件,从不触发选择事件。 @RyanMann 奇怪,我已经在 FF、Chrome 和 Edge 中进行了测试,它似乎对我有用。我建议您尝试将延迟从“100ms”增加到“200ms”。我怀疑更改正在触发,但它花费的时间超过 100 毫秒,因此首先触发了取消。 Lmk 是否可行,我也许可以提出其他建议。 谢谢,我已经厌倦了处理它,所以我只是重新设计了我的工作流程,不需要知道是否点击了取消。我想知道是否单击了取消,以便我可以停止显示模态叠加加载窗口。所以我只是将其更改为在更改事件中出现新文件之前不显示覆盖。【参考方案3】:

您可以在输入字段上创建一个 jquery 更改侦听器,并通过该字段的值检测用户取消或关闭上传窗口。

这是一个例子:

//when upload button change
$('#upload_btn').change(function()
    //get uploaded file
    var file = this.files[0];

    //if user choosed a file
    if(file)
        //upload file or perform your desired functiuonality
    else
        //user click cancel or close the upload window
    
);

【讨论】:

不知道为什么这被否决了——这就是我想要的。谢谢! ?【参考方案4】:

我需要这样做,我最终创建了一个非常简单的解决方案。可以看这里:http://jsfiddle.net/elizeubh2006/1w711q0y/5/

(但只有在第一次选择文件后才会起作用。对我来说这很有用,因为即使用户单击取消也会调用 onchange 事件。用户选择了一个文件,然后单击以选择另一个文件,但取消了并调用了 onchange。)

   $("#inputFileId").on("change", function() 
 var x = document.getElementById('inputFileId');
    if(x.files.length == 0)
    
    alert('cancel was pressed');
    
    else
    
        alert(x.files[0].name);
    

);

【讨论】:

很遗憾,这不适用于 ios 移动 Safari。 它不起作用,因为只有在选择文件时才会触发更改,而且所选文件应该与以前选择的文件不同。 "只有在第一次选择文件后才能工作" -> jsfiddle.net/elizeubh2006/1w711q0y/5 “仅在第一次选择后才有效” 有多少人甚至不看这个问题并试图回答它?... OP 想要一个取消按钮的事件而不是文件更改。取消不会触发更改事件。如果用户取消它,您的代码将永远不会运行。

以上是关于如何检测何时单击文件输入的取消?的主要内容,如果未能解决你的问题,请参考以下文章

如何检测何时单击矩形对象、图像或精灵

您如何检测“搜索”HTML5 输入的清除?

选择:通过单击选定文本取消选择的 isCollapsed 值不正确

如何检测何时单击/点击 MapMarker? [复制]

如何检测用户何时单击 lyft 按钮 swift

如何检测UIWebView中的单击文件链接?