【问题标题】:Cancel event on input type="file"取消输入类型=“文件”的事件
【发布时间】:2016-04-23 15:55:48
【问题描述】:

我正在使用用于上传的标准文件输入,并且我正在寻找一种在用户单击/点击“取消”按钮上的输入(或从选择文件中退出)时将函数附加到事件的方法对话。

我找不到任何可以在所有浏览器和平台上一致运行的事件。

我已阅读此问题的答案:Capturing cancel event on input type=file 但它们不起作用,因为在大多数浏览器中取消选择文件对话框时更改事件不会触发。

我正在寻找一个纯 js 解决方案,但也对 jquery 解决方案开放。

有人成功解决了这个问题吗?

【问题讨论】:

  • 由于.change()不是所有浏览器都执行的,应该寻找被调用的事件。您是否特别希望使用他们的浏览器?
  • 在 FF 中使用此 jsfiddle.net/Twisty/j18td9cs 进行一些测试,并且由于“取消”是浏览器对话框的元素,我只能检查是否选择了文件。我注意到如果我选择一个文件,然后第二次浏览,然后点击取消,它会保留文件值......所以这在这里没有帮助。可以在页面中创建自己的取消按钮。如果选择取消,则尝试查看是否有更新或返回,例如 confirm() 或 prompt()。

标签: javascript jquery file-upload


【解决方案1】:

一些研究表明,无法检测何时在“文件选择”对话框窗口中选择了“取消”。您可以使用onchange 或onblur 来检查是否已选择文件或是否已将某些内容添加到输入value。

这可能看起来像:https://jsfiddle.net/Twisty/j18td9cs/

HTML

<form>
  Select File:
  <input type="file" name="test1" id="testFile" />
  <button type="reset" id="pseudoCancel">
    Cancel
  </button>
</form>

JavaScript

var inputElement = document.getElementById("testFile");
var cancelButton = document.getElementById("pseudoCancel");
var numFiles = 0;

inputElement.onclick = function(event) {
  var target = event.target || event.srcElement;
  console.log(target, "clicked.");
  console.log(event);
  if (target.value.length == 0) {
    console.log("Suspect Cancel was hit, no files selected.");
    cancelButton.onclick();
  } else {
    console.log("File selected: ", target.value);
    numFiles = target.files.length;
  }
}

inputElement.onchange = function(event) {
  var target = event.target || event.srcElement;
  console.log(target, "changed.");
  console.log(event);
  if (target.value.length == 0) {
    console.log("Suspect Cancel was hit, no files selected.");
    if (numFiles == target.files.length) {
      cancelButton.onclick();
    }
  } else {
    console.log("File selected: ", target.value);
    numFiles = target.files.length;
  }
}

inputElement.onblur = function(event) {
  var target = event.target || event.srcElement;
  console.log(target, "changed.");
  console.log(event);
  if (target.value.length == 0) {
    console.log("Suspect Cancel was hit, no files selected.");
    if (numFiles == target.files.length) {
      cancelButton.onclick();
    }
  } else {
    console.log("File selected: ", target.value);
    numFiles = target.files.length;
  }
}


cancelButton.onclick = function(event) {
  console.log("Pseudo Cancel button clicked.");
}

我建议您制作自己的取消或重置按钮来重置表单或清除输入中的值。

【讨论】:

  • 这不适用于 Firefox。 :( 如果以前未选择文件,只要单击“浏览”按钮,它就会记录到控制台 Suspect Cancel was hit, no files selected. 和 Pseudo Cancel button clicked.。在浏览对话框中单击取消时,没有任何反应。如果以前选择了文件,如当我单击“浏览”时,它会记录我选择了上一个文件,即使我仍在对话框中。
  • 用FF写的。将不得不再次检查。搞定了你的测试代码?
  • 感谢先生的快速回复,我只是使用上面的小提琴我没有单独写任何东西。
  • @Noitidart 看起来 FF 会触发focus,然后是blur,然后是change(如果用户选择了一个文件)。所以click 和blur 事件打断了这里的逻辑。更新 jQuery:jsfiddle.net/Twisty/btf1m9nc
  • 浏览器也可能存在输入元素不可见的问题。在我的情况下,我可以隐藏我的输入元素,不显示或隐藏可见性,并使用带标签的按钮触发对话框。 onfocus 事件仍将被触发(至少对于 Mac 上的 Chrome)。但在智能手机(iPhone 上的 Chrome 或 HTC U11)上的测试显示,对于这种特殊情况,它们不会触发 onfocus。它可能不仅与隐藏输入有关,而且我通过带标签的按钮而不是输入元素本身触发对话框。
【解决方案2】:

我有一个完美的解决方案。

焦点事件将在更改事件之前执行。

所以我需要使用 setTimeout 让 focus 方法比 change 方法执行得晚。

const createUpload = () => {
    let lock = false
    return new Promise((resolve, reject) => {
        // create input file
        const el = document.createElement('input')
        el.id = +new Date()
        el.style.display = 'none'
        el.setAttribute('type', 'file')
        document.body.appendChild(el)

        el.addEventListener('change', () => {
            lock = true
            const file = el.files[0]
            
            resolve(file)
            // remove dom
            document.body.removeChild(document.getElementById(el.id))
        }, { once: true })

        // file blur
        window.addEventListener('focus', () => {
            setTimeout(() => {
                if (!lock && document.getElementById(el.id)) {
                    reject(new Error('onblur'))
                    // remove dom
                    document.body.removeChild(document.getElementById(el.id))
                }
            }, 300)
        }, { once: true })

        // open file select box
        el.click()
    })
}



try {
    const file = createUpload()
    console.log(file)
} catch(e) {
    console.log(e.message) // onblur
}

【讨论】:

    【解决方案3】:

    在您的“inputElement.onclick”中,您应该设置一个标志(在我的情况下,我设置了 window.inputFileTrueClosed = true),这样您就可以在按下该类型事件的“取消”按钮后检测窗口何时获得焦点。以下检测窗口是否再次获得焦点:这意味着可能已按下“取消”按钮:

    var isChrome = !!window.chrome;
    
        window.addEventListener('focus', function (e) {
           
        
        if(window.inputFileTrueClosed != false){
          
            if(isChrome == true){
               setTimeout(
                        function() 
                        {
                             if(window.inputFileTrueClosed != false){
       //if it is Chrome we have to wait because file.change(function(){... comes after "the window gets the focus"
                             window.inputFileTrueClosed = false; 
                             }
                        }, 1000);
    }
    else
    {
            // if it is NOT Chrome (ex.Safari) do something when the "cancel" button is pressed.
    
            window.inputFileTrueClosed = false;
            
        }
    }
    })
    

    显然,该窗口获得了许多其他事件的焦点,但您可以选择需要检测的标志。

    【讨论】:

      【解决方案4】:

      当我们选择文件时,会调用以下事件 -

      场景一:点击选择文件后点击取消时

      Focus事件值=""

      Click事件值=""

      Blur事件值=""

      Focus事件值=""

      Blur event value=""(当用户点击某处时)

      场景2:当文件被选中时-

      Focus事件值=""

      Click事件值=""

      Blur事件值=""

      Focus事件值=""

      Changeevent value="filevalue"

      Blurevent value="filevalue"

      Focusevent value="filevalue"

      Blur event value="filevalue"(当用户点击某处时)

      我们在这里看到,当在焦点事件之后调用了Blur事件(最后一个事件),并且没有文件的值意味着点击了取消按钮。

      我的方案是在文件加载时将提交按钮文本更改为“请稍候” currentEvent 变量用于保存当前事件值,无论是点击还是聚焦 如果 currentEvent = focus 且文件值为 null 则表示单击了取消按钮。

      Javascript
      
      var currentEvent = "focus";
      
      function onFileBrowse() {    
          var vtbFile = $('#uploadFile')[0].files[0];
      
          if (vtbFile != undefined) {
              var extension = vtbFile.name.split('.').pop().toLowerCase();
              var valError = "";
      
              if (extension === 'xlsx' || extension === 'xlsb' || extension === 'csv') {
                  if (vtbFile.size === 0)
                      valError = "File '" + vtbFile.name + "' is empty";
              }
              else
                  valError = "Extension '" + extension + "' is not supported.";
      
              if (valError !== "") {            
                  alert("There was an issue validating the file. " + valError, 20000);
              }        
          }
          //hide Indicator
          var buttonUpload = document.getElementById('btnUploadTB');
          buttonUpload.innerText = "Submit";
      };
      
      
      function fileClick() {
          //show Indicator
          var buttonUpload = document.getElementById('btnUploadTB');
          buttonUpload.innerText = "Please wait..";
          
          document.getElementById('uploadFile').value = null;   
          currentEvent = "click";
      }
      function fileFocus() {
          currentEvent = "focus";
      }
      
      function fileBlur() {
          
          if (!document.getElementById('uploadFile').value && currentEvent == "focus") {
              console.log('blur' + 'change to submit');
              //hide Indicator
              var buttonUpload = document.getElementById('btnUploadTB');
              buttonUpload.innerText = "Submit";
          }
      }
      HTML
      
      <input class="k-button k-upload-button" type="file" id="uploadFile" name="uploadFile"
          accept=".csv,.xlsx,.xlsb" onChange='onFileBrowse()' onclick="fileClick()" onfocus="fileFocus()" onblur="fileBlur()" />
      
      <button id="btnUploadTB" type="button" class="btn btn-default" onclick="uploadTBFile()" style="width:28%;margin-left: 3px;">Submit</button>

      【讨论】:

      • 由于事件之间存在不同的延迟,因此此解决方法与“通知”对话框更改有一些延迟一样好(在我的情况下为 500 秒 - 这是很多并且使 ui 滞后),但在这种情况下,需要更少的代码。/
      【解决方案5】:

      我遇到了问题,我单击了输入 type="file" 元素上的取消按钮,并且希望该函数什么都不做。如果选择了某些内容并且我单击了打开按钮,那么我希望我的函数执行某些操作。该示例仅显示了该方法,我在它打开后剥离了我对它所做的事情。我输入警报只是为了让您在单击取消时看到没有从对话框返回的文件名。 这是我使用的一种方法,它很简单但很有效。

       function openFileOnClick(){
          document.getElementById("fileSelector").value = "";
          document.getElementById("fileSelector").files.length = 0;            
          document.getElementById("fileSelector").click();
          if(document.getElementById("fileSelector").files.length >= 1){
              alert(document.getElementById("fileSelector").value);
              //Do something 
          }
          else{
              alert(document.getElementById("fileSelector").value);
              //Cancel button has been called.
          }
      }
      <html>
      <head>
      </head>
      <body>
      <input type="file" id="fileSelector" name="fileSelector" value="" style="display:none;"  />
      <input type="button" value="Open File" name="openFile" onclick="openFileOnClick();" />
      </body>
      </html>

      【讨论】:

      • 您能否更详细地解释一下您的代码 sn-p 的作用或工作原理?
      • 对于这样的事情,提供一个小提琴很有帮助,这样我们就可以观察行为
      • @DJones,如果您可以编辑您的答案并将该信息添加到其中,那就太好了!
      • 我把它转换成一个 sn-p 来测试它。在对话框完成之前弹出警报。也许您在为演示而剥离代码时删除了一些重要的东西?
      • 不,这段代码只是在 IE 中运行。在普通浏览器中,点击会触发异步代码。只有在 IE 中点击功能是同步的
      【解决方案6】:

      在使用 jQuery 实现图像上传表单的自动提交后,我遇到了类似的问题。如果用户取消对话,它会发送一个空白。所需要的只是在同一个脚本中检测这个空值:

      $('#upload-portrait-input').on('change', function(){
         if ( $(this).val() != '' )
          {
          $('#portraitForm').submit();
          }
         else { // do something when the user clicks cancel
          }
       });
      

      【讨论】:

        【解决方案7】:

        无论用户是否正在浏览文件资源管理器(活动/非活动),我都需要为我的文件上传设置不同的样式。只有click 和blur 事件。只需考虑当用户单击“取消”按钮时,需要在文件上传之外单击(在我的情况下为button)。

        这是我针对 Angular 的解决方案,但我想任何人都可以抓住这个想法并将其与您最喜欢的框架/库/Vanilla JS 相适应。

        <!-- HTML - Angular -->
        
        <input hidden type="file" #fileInput (change)="onFileUpload($event.target.files)">
        <button (click)="initBrowsingFlags(); fileInput.click()" (blur)="onBlur()" [class.browsing]="browsing">Upload</button>
        
        // Typescript - Angular
        
        /** Whether the user is browsing the file explorer. */
        browsing = false;
        
        /** 
         * If a 2nd `blur` event is emitted while {@link browsing} is still true, it means that the user
         * clicked the Cancel button on the file explorer.
         * */
        alreadyOneBlurEventWhileBrowsing = false;
        
        onFileUpload(files: FileList) {
          // ...
          this.resetBrowsingFlags();
        }
        
        onBlur() {
          if (!this.browsing) return;
        
          if (this.onCancelClickWhileBrowsing) {
            this.resetBrowsingFlags();
          } else {
            this.onCancelClickWhileBrowsing = true;
          }
        }
        
        initBrowsingFlags() {
          this.browsing = true;
          this.alreadyOneBlurEventWhileBrowsing= false;
        }
        
        resetBrowsingFlags() {
          this.browsing = false;
          this.alreadyOneBlurEventWhileBrowsing= false;
        }
        

        【讨论】:

          猜你喜欢
          • 2012-04-25
          • 2021-09-01
          • 1970-01-01
          • 2020-03-31
          • 1970-01-01
          • 1970-01-01
          • 2012-03-13
          • 2020-11-26
          • 2022-10-24
          相关资源
          最近更新 更多