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

时间:2011-01-07 17:46:26

标签: html file input

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

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

34 个答案:

答案 0 :(得分:38)

虽然不是一个直接的解决方案,也不是因为它只是(据我测试过)与onfocus一起使用(需要一个非常有限的事件阻塞),你可以通过以下方式实现它:

document.body.onfocus = function(){ /*rock it*/ }

有什么好处的,就是你可以在文件事件中及时附加/分离它,并且它似乎也可以正常使用隐藏输入(如果你使用可视化的解决方法来处理糟糕的默认输入,那么这是一个明确的好处类型= '文件')。之后,您只需要确定输入值是否已更改。

一个例子:

var godzilla = document.getElementById('godzilla')

godzilla.onclick = charge

function charge()
{
    document.body.onfocus = roar
    console.log('chargin')
}

function roar()
{
    if(godzilla.value.length) alert('ROAR! FILES!')
    else alert('*empty wheeze*')
    document.body.onfocus = null
    console.log('depleted')
}

查看实际操作:http://jsfiddle.net/Shiboe/yuK3r/6/

可悲的是,它似乎只适用于webkit浏览器。也许其他人可以找出firefox / IE解决方案

答案 1 :(得分:17)

你不能。

文件对话框的结果不会向浏览器公开。

答案 2 :(得分:8)

/* Tested on Google Chrome */
$("input[type=file]").bind("change", function() {
    var selected_file_name = $(this).val();
    if ( selected_file_name.length > 0 ) {
        /* Some file selected */
    }
    else {
        /* No file selected or cancel/close
           dialog button clicked */
        /* If user has select a file before,
           when they submit, it will treated as
           no file selected */
    }
});

答案 3 :(得分:6)

所以,自从我提出一个新颖的解决方案以来,我就把这个问题放到了这个问题上。我有一个Progressive Web App,允许用户捕获照片和视频并上传它们。我们尽可能使用WebRTC,但对于支持较少的设备*咳嗽Safari咳嗽*,可以回到HTML5文件拣选器。 如果您专注于使用原生相机直接捕捉照片/视频的Android / iOS移动网络应用程序,那么这是我遇到过的最佳解决方案。

这个问题的症结在于,当页面加载时,filenull,但是当用户打开对话框并按下"取消"时,{{ 1}}仍然是file,因此它没有"更改",所以没有"更改"事件被触发。对于桌面而言,这并不是太糟糕,因为大多数桌面用户界面并不依赖于知道取消被调用的时间,而是移动用户界面会调出相机拍摄照片/视频非常取决于知道何时按下取消。

我最初使用null事件来检测用户何时从文件选择器返回,这适用于大多数设备,但iOS 11.3打破了该事件,因为该事件未被触发。

概念

我对此的解决方案是*颤抖*来测量CPU时间以确定页面当前是在前台还是后台。在移动设备上,处理时间将提供给当前位于前台的应用程序。当摄像机可见时,它将窃取CPU时间并降低浏览器的优先级。我们需要做的就是衡量我们的页面处理时间,当相机启动时,我们的可用时间将急剧下降。当相机被取消(取消或以其他方式)时,我们的可用时间会重新启动。

实施

我们可以通过使用document.body.onfocus以X毫秒调用回调来测量CPU时序,然后测量实际调用它所需的时间。浏览器在X毫秒后将永远不会完全调用它,但如果它合理接近则我们必须在前台。如果浏览器距离很远(比请求的速度慢10倍),那么我们必须在后台。这个的基本实现是这样的:

setTimeout()

我在最新版本的iOS和Android上对此进行了测试,通过在function waitForCameraDismiss() { const REQUESTED_DELAY_MS = 25; const ALLOWED_MARGIN_OF_ERROR_MS = 25; const MAX_REASONABLE_DELAY_MS = REQUESTED_DELAY_MS + ALLOWED_MARGIN_OF_ERROR_MS; const MAX_TRIALS_TO_RECORD = 10; const triggerDelays = []; let lastTriggerTime = Date.now(); return new Promise((resolve) => { const evtTimer = () => { // Add the time since the last run const now = Date.now(); triggerDelays.push(now - lastTriggerTime); lastTriggerTime = now; // Wait until we have enough trials before interpreting them. if (triggerDelays.length < MAX_TRIALS_TO_RECORD) { window.setTimeout(evtTimer, REQUESTED_DELAY_MS); return; } // Only maintain the last few event delays as trials so as not // to penalize a long time in the camera and to avoid exploding // memory. if (triggerDelays.length > MAX_TRIALS_TO_RECORD) { triggerDelays.shift(); } // Compute the average of all trials. If it is outside the // acceptable margin of error, then the user must have the // camera open. If it is within the margin of error, then the // user must have dismissed the camera and returned to the page. const averageDelay = triggerDelays.reduce((l, r) => l + r) / triggerDelays.length if (averageDelay < MAX_REASONABLE_DELAY_MS) { // Beyond any reasonable doubt, the user has returned from the // camera resolve(); } else { // Probably not returned from camera, run another trial. window.setTimeout(evtTimer, REQUESTED_DELAY_MS); } }; window.setTimeout(evtTimer, REQUESTED_DELAY_MS); }); } 元素上设置属性来启动原生相机。

<input />

这实际上比我预期的要好很多。它通过请求在25毫秒内调用计时器来运行10次试验。然后它测量实际调用的时间,如果10次试验的平均值小于50毫秒,我们假设我们必须在前台并且相机已经消失。如果它大于50毫秒,那么我们仍然必须在后台并且应该继续等待。

一些其他细节

我使用<input type="file" accept="image/*" capture="camera" /> <input type="file" accept="video/*" capture="camcorder" /> 而不是setTimeout(),因为后者可以对多个调用进行排队,这些调用会在彼此之后立即执行。这可能会大大增加我们数据中的噪音,所以我坚持使用setInterval(),尽管这样做有点复杂。

这些特殊数字对我来说效果很好,不过我至少看过一次过早检测到相机消失的情况。我相信这是因为相机可能打开速度很慢,并且设备在实际变为背景之前可能会进行10次试验。在开始此功能之前添加更多试验或等待大约25-50毫秒可能是一种解决方法。

桌面

不幸的是,这对桌面浏览器来说并不适用。从理论上讲,同样的技巧是可行的,因为他们确实优先考虑当前页面而不是背景页面。然而,许多台式机都有足够的资源来保持页面全速运行,即使在后台运行时也是如此,因此这种策略在实践中并没有真正起作用。

替代解决方案

一个替代解决方案,没有多少人提到我做过探索是嘲笑setTimeout()。我们从FileList中的null开始,如果用户打开相机并取消,则会返回<input />,这不是更改,也不会触发任何事件。一种解决方案是在页面开始时为null分配一个虚拟文件,因此设置为<input />将是一个触发相应事件的更改。

不幸的是,官方无法创建nullFileList元素特别需要<input />,除了{{{{}}之外不会接受任何其他值1}}。当然,不能直接构造FileList个对象,而是针对某些旧的安全问题,这个问题显然不再相关。获取null元素之外的唯一方法是利用复制粘贴数据的hack伪造剪贴板事件,该事件可以包含FileList对象(您基本上是伪造的)一个拖放文件在你的网站上的事件)。这在Firefox中是可行的,但对于iOS Safari则不行,所以它对我的特定用例来说是不可行的。

浏览器,请...

毋庸置疑,这显然是荒谬的。网页被给予关键UI元素已经改变的零通知这一事实简直是可笑的。这实际上是规范中的一个错误,因为它从来没有用于全屏媒体捕获UI,也没有触发&#34;更改&#34;事件技术上到规范。

然而,浏览器厂商能否认识到这一点的现实?这可以通过新的&#34;完成&#34;来解决。即使没有发生变化也会触发的事件,或者您可以触发&#34;更改&#34;无论如何。是的,那将违反规范,但对于我来说,在JavaScript方面重复更改事件是微不足道的,但从根本上不可能发明我自己的&#34;完成&#34;事件。即使我的解决方案实际上只是启发式,如果不能保证浏览器的状态。

目前,这个API基本上无法用于移动设备,我认为相对简单的浏览器更改可以让网络开发人员更加轻松*离开肥皂盒*。

答案 4 :(得分:6)

当您选择文件并单击打开/取消时,input元素将失去焦点,即blur。假设value的初始input为空,则blur处理程序中的任何非空值都表示正常,而空值表示取消。

更新:隐藏blur时未触发input。所以不能在基于IFRAME的上传中使用此技巧,除非您想暂时显示input

答案 5 :(得分:5)

也可以听一下点击事件。

根据Shiboe的例子,这是一个jQuery示例:

var godzilla = $('#godzilla');
var godzillaBtn = $('#godzilla-btn');

godzillaBtn.on('click', function(){
    godzilla.trigger('click');
});

godzilla.on('change click', function(){

    if (godzilla.val() != '') {
        $('#state').html('You have chosen a Mech!');    
    } else {
        $('#state').html('Choose your Mech!');
    }

});

您可以在此处看到它:http://jsfiddle.net/T3Vwz

答案 6 :(得分:5)

如果选择与以前相同的文件,则可以捕获取消,然后单击取消:在这种情况下。

你可以这样做:

<input type="file" id="myinputfile"/>
<script>
document.getElementById('myinputfile').addEventListener('change', myMethod, false);
function myMethod(evt) {
  var files = evt.target.files; 
  f= files[0];
  if (f==undefined) {
     // the user has clicked on cancel
  }
  else if (f.name.match(".*\.jpg")|| f.name.match(".*\.png")) {
     //.... the user has choosen an image file
     var reader = new FileReader();
     reader.onload = function(evt) { 
        try {
        myimage.src=evt.target.result;
            ...
         } catch (err) {
            ...
         }
     };
  }     
  reader.readAsDataURL(f);          
</script>

答案 7 :(得分:5)

最简单的方法是检查临时内存中是否有任何文件。如果您想在每次用户单击文件输入时获取更改事件,您可以触发它。

var yourFileInput = $("#yourFileInput");

yourFileInput.on('mouseup', function() {
    $(this).trigger("change");
}).on('change', function() {
    if (this.files.length) {
        //User chose a picture
    } else {
        //User clicked cancel
    }
});

答案 8 :(得分:4)

这些解决方案大多不适合我。

问题是你永远不知道会触发哪个事件, 它是click还是change?您无法承担任何订单,因为它可能取决于浏览器的实施。

至少在Opera和Chrome中(2015年末)click会在“填充”之前触发。输入文件,因此在files.length != 0之后延迟click延迟之前,您永远不会知道change的长度。

这是代码:

var inputfile = $("#yourid");

inputfile.on("change click", function(ev){
    if (ev.originalEvent != null){
        console.log("OK clicked");
    }
    document.body.onfocus = function(){
        document.body.onfocus = null;
        setTimeout(function(){
            if (inputfile.val().length === 0) console.log("Cancel clicked");
        }, 1000);
    };
});

答案 9 :(得分:2)

如果Shiboe的解决方案适用于移动webkit,那么Shiboe的解决方案将是一个很好的解决方案,但事实并非如此。我能想到的是在打开文件输入窗口时向某个dom对象添加一个mousemove事件监听器,如下所示:

 $('.upload-progress').mousemove(function() {
        checkForFiles(this);
      });
      checkForFiles = function(me) {
        var filefield = $('#myfileinput');
        var files = filefield.get(0).files;
        if (files == undefined || files[0] == undefined) $(me).remove(); // user cancelled the upload
      };

当文件对话框打开时,mousemove事件将从页面中被阻止,当其关闭的事件检查文件输入中是否有任何文件时。在我的情况下,我想要一个活动指示器阻止事件,直到文件上传,所以我只想删除我的取消指示器。

然而,这并不能解决移动问题,因为没有移动鼠标。我的解决方案并不完美,但我认为它足够好。

       $('.upload-progress').bind('touchstart', function() {
        checkForFiles(this);
      });

现在我们正在监听屏幕上的触摸以执行相同的文件检查。我非常有信心在取消后很快就会将用户的手指放在屏幕上并关闭此活动指示器。

还可以在文件输入更改事件上添加活动指示器,但在移动设备上,选择图像和更改事件触发之间通常会有几秒钟的延迟,因此活动指示器的用户体验要好得多在流程开始时显示。

答案 10 :(得分:2)

在新的File System Access API将会使我们的生活又简单:)

try {
    const [fileHandle] = await window.showOpenFilePicker();
    const file = await fileHandle.getFile();
    // ...
}
catch (e) {
    console.log('Cancelled, no file selected');
}

浏览器支持非常有限(一月,2021)。示例代码工作良好在Chrome桌面86。

答案 11 :(得分:2)

我知道这是一个非常古老的问题但是为了防止有人,我发现在使用onmousemove事件来检测取消时,有必要在短时间内测试两个或更多这样的事件。 这是因为每次将光标移出选择文件对话框窗口并且每次将其移出主窗口并重新进入时,浏览器(Chrome 65)都会生成单个onmousemove事件。 一个简单的鼠标移动事件计数器,再加上一个短暂的超时时间,将计数器重置为零,这是一种享受。

答案 12 :(得分:1)

在我的情况下,我必须在用户选择图像时隐藏提交按钮。

这就是我的想法:

$(document).on('click', '#image-field', function(e) {
  $('.submit-button').prop('disabled', true)
})
$(document).on('focus', '#image-field'), function(e) {
  $('.submit-button').prop('disabled', false)
})

#image-field是我的文件选择器。当有人点击它时,我会禁用表单提交按钮。关键是,当文件对话框关闭时 - 无关紧要,他们选择一个文件或取消 - #image-field得到焦点,所以我听那个事件。

<强>更新

我发现,这在safari和poltergeist / phantomjs中不起作用。如果您想实施此信息,请考虑此信息。

答案 13 :(得分:1)

结合Shiboe和alx的解决方案,我获得了最可靠的代码:

var selector = $('<input/>')
   .attr({ /* just for example, use your own attributes */
      "id": "FilesSelector",
      "name": "File",
      "type": "file",
      "contentEditable": "false" /* if you "click" on input via label, this prevents IE7-8 from just setting caret into file input's text filed*/
   })
   .on("click.filesSelector", function () {
      /* do some magic here, e.g. invoke callback for selection begin */
      var cancelled = false; /* need this because .one calls handler once for each event type */
      setTimeout(function () {
         $(document).one("mousemove.filesSelector focusin.filesSelector", function () {
            /* namespace is optional */
            if (selector.val().length === 0 && !cancelled) {
               cancelled = true; /* prevent double cancel */
               /* that's the point of cancel,   */
            }
         });                    
      }, 1); /* 1 is enough as we just need to delay until first available tick */
   })
   .on("change.filesSelector", function () {
      /* do some magic here, e.g. invoke callback for successful selection */
   })
   .appendTo(yourHolder).end(); /* just for example */

通常,mousemove事件可以解决这个问题,但是如果用户点击了一下,那么:

  • 通过转义键取消文件打开对话框(不移动鼠标),再次准确点击再次打开文件对话框...
  • 将焦点切换到任何其他应用程序,而不是回到浏览器的文件打开对话框并关闭它,而不是通过输入或空格键再次打开...

...我们没有获得mousemove事件因此没有取消回调。此外,如果用户取消第二个对话框并移动鼠标,我们将获得2个取消回调。 幸运的是,特殊的jQuery focusIn事件在两种情况下都会冒泡到文档中,帮助我们避免这种情况。唯一的限制是,如果有人阻止focusIn事件。

答案 14 :(得分:1)

我看到我的回答会过时,但从未如此。 我遇到了同样的问题。所以这是我的解决方案。 剪辑最有用的代码是KGA的代码。但它并不完全有效,而且有点复杂。但我简化了它。

此外,主要的麻烦制造者是这样的事实,即“焦点”之后“变化”事件不会立即出现,所以我们必须等待一段时间。

“#appendfile” - 用户点击以追加新文件。 Hrefs获得焦点事件。

$("#appendfile").one("focusin", function () {
    // no matter - user uploaded file or canceled,
    // appendfile gets focus
    // change doesn't come instantly after focus, so we have to wait for some time
    // wrapper represents an element where a new file input is placed into
    setTimeout(function(){
        if (wrapper.find("input.fileinput").val() != "") {
            // user has uploaded some file
            // add your logic for new file here
        }
        else {
            // user canceled file upload
            // you have to remove a fileinput element from DOM
        }
    }, 900);
});

答案 15 :(得分:1)

您只能在有限的情况下检测到这一点。具体来说,在chrome中如果先前选择了一个文件,然后点击了文件对话框并取消了点击,则Chrome会清除该文件并触发onChange事件。

https://code.google.com/p/chromium/issues/detail?id=2508

在这种情况下,您可以通过处理onChange事件并检查 文件 属性来检测这一点。

答案 16 :(得分:1)

file - 字段令人沮丧,并没有对很多事件作出反应(模糊很可爱)。我看到很多人建议以change为导向的解决方案并将其投降。

change确实有效,但它有一个重大缺陷(与我们想要发生的事情相比)。

当您刚刚加载包含文件字段的页面时,请打开该框并按取消。没什么,令人沮丧的是,改变了

我选择做的是加载门控状态。

  • 在我的案例中,section#after-image形式的下一部分是隐藏的。当我的file field更改时,会显示上传按钮。成功上传后,会显示section#after-image
  • 如果用户加载,打开文件对话框,然后取消,他们永远不会看到上传按钮。
  • 如果用户选择了文件,则会显示上传按钮。如果他们然后打开对话框并取消,则此取消将触发change事件,并且我可以(并且确实)重新隐藏我的上传按钮,直到选择了正确的文件。

我很幸运,这个封闭状态已经是我的形式的设计。您不需要使用相同的样式,只需最初隐藏上传按钮并在更改时将隐藏字段或javascript变量设置为您可以在提交时监控的内容。

我尝试在字段交互之前更改文件[0]的值。这对于改变没有任何作用。

是的,change有效,至少和我们一样好。由于显而易见的原因,文件字段是安全的,但是对善意的开发人员来说是沮丧的。

它不符合我的目的,但您可能会onclick加载警告提示(不是alert(),因为这会停止页面处理),并隐藏如果触发更改并且files [0]为null。如果未触发更改,则div保持其状态。

答案 17 :(得分:1)

以下似乎对我有用(在桌面,Windows上):

var openFile = function (mimeType, fileExtension) {
    var defer = $q.defer();
    var uploadInput = document.createElement("input");
    uploadInput.type = 'file';
    uploadInput.accept = '.' + fileExtension + ',' + mimeType;

    var hasActivated = false;

    var hasChangedBeenCalled = false;
    var hasFocusBeenCalled = false;
    var focusCallback = function () {
        if (hasActivated) {
            hasFocusBeenCalled = true;
            document.removeEventListener('focus', focusCallback, true);
            setTimeout(function () {
                if (!hasChangedBeenCalled) {
                    uploadInput.removeEventListener('change', changedCallback, true);
                    defer.resolve(null);
                }
            }, 300);
        }
    };

    var changedCallback = function () {
        uploadInput.removeEventListener('change', changedCallback, true);
        if (!hasFocusBeenCalled) {
            document.removeEventListener('focus', focusCallback, true);
        }
        hasChangedBeenCalled = true;
        if (uploadInput.files.length === 1) {
            //File picked
            var reader = new FileReader();
            reader.onload = function (e) {
                defer.resolve(e.target.result);
            };
            reader.readAsText(uploadInput.files[0]);
        }
        else {
            defer.resolve(null);
        }
    };
    document.addEventListener('focus', focusCallback, true); //Detect cancel
    uploadInput.addEventListener('change', changedCallback, true); //Detect when a file is picked
    uploadInput.click();
    hasActivated = true;
    return defer.promise;
}

这确实使用angularjs $ q,但如果需要,您应该能够将其替换为任何其他promise框架。

在IE11,Edge,Chrome,Firefox上进行了测试,但它似乎无法在Android平板电脑上使用Chrome,因为它不会触发Focus事件。

答案 18 :(得分:0)

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

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

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

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

  • choose:用户选择了一个文件(如果他们选择了仍然会触发) 再次使用相同的文件)。
  • 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。

答案 19 :(得分:0)

如果您已经需要JQuery,则此解决方案可能会起作用(这与我实际需要的代码完全相同,尽管使用Promise只是强制代码等待文件选择已解决):< / p>

await new Promise(resolve => {
    const input = $("<input type='file'/>");
    input.on('change', function() {
        resolve($(this).val());
    });
    $('body').one('focus', '*', e => {
        resolve(null);
        e.stopPropagation();
    });
    input.click();

});

答案 20 :(得分:0)

隐藏输入文件选择解决方案

我在这里寻找隐藏输入文件上传的解决方案时,我相信这是寻找一种检测文件输入取消的方法的最常见原因(打开文件对话框->如果已选择文件)然后运行一些代码,否则什么也不做),这是我的解决方案:

var fileSelectorResolve;
var fileSelector = document.createElement('input');
fileSelector.setAttribute('type', 'file');

fileSelector.addEventListener('input', function(){
   fileSelectorResolve(this.files[0]);
   fileSelectorResolve = null;
   fileSelector.value = '';
});

function selectFile(){
   if(fileSelectorResolve){
      fileSelectorResolve();
      fileSelectorResolve = null;
   }
   return new Promise(function(resolve){
      fileSelectorResolve = resolve;
      fileSelector.dispatchEvent(new MouseEvent('click'));
   });
}

用法示例:

请注意,如果未选择任何文件,则仅在再次调用selectFile()(或者从其他地方调用fileSelectorResolve())后,第一行才会返回。

async function logFileName(){
   const file = await selectFile();
   if(!file) return;
   console.log(file.name);
}

另一个例子:

async function uploadFile(){
   const file = await selectFile();
   if(!file) return;

   // ... make an ajax call here to upload the file ...
}

答案 21 :(得分:0)

此线程中有几种建议的解决方案,难以检测用户何时单击文件选择框上的“取消”按钮是一个影响很多人的问题。

事实是,没有100%可靠的方法来检测用户是否单击了文件选择框上的“取消”按钮。但是,有几种方法可靠地检测用户是否已向输入文件添加文件。所以这是这个答案的基本策略!

我之所以决定添加此答案,是因为其他答案显然不适用于大多数浏览器或无法在移动设备上使用。

简而言之,代码基于3点:

  1. 输入文件最初是在js的“内存”中动态创建的 (目前我们不将其添加到“ HTML”中);
  2. 添加文件后,将输入文件添加到HTML,否则什么也不会发生;
  3. 通过从以下位置删除输入文件来删除文件: 通过特定事件的HTML,这意味着 通过删除旧文件来完成文件的“编辑” /“修改” 输入文件并创建一个新文件。

为更好地理解,请查看下面的代码和说明。

[...]
<button type="button" onclick="addIptFl();">ADD INPUT FILE!</button>
<span id="ipt_fl_parent"></span>
[...]
function dynIptFl(jqElInst, funcsObj) {
    if (typeof funcsObj === "undefined" || funcsObj === "") {
        funcsObj = {};
    }
    if (funcsObj.hasOwnProperty("before")) {
        if (!funcsObj["before"].hasOwnProperty("args")) {
            funcsObj["before"]["args"] = [];
        }
        funcsObj["before"]["func"].apply(this, funcsObj["before"]["args"]);
    }
    var jqElInstFl = jqElInst.find("input[type=file]");

    // NOTE: Open the file selection box via js. By Questor
    jqElInstFl.trigger("click");

    // NOTE: This event is triggered if the user selects a file. By Questor
    jqElInstFl.on("change", {funcsObj: funcsObj}, function(e) {

        // NOTE: With the strategy below we avoid problems with other unwanted events
        // that may be associated with the DOM element. By Questor
        e.preventDefault();

        var funcsObj = e.data.funcsObj;
        if (funcsObj.hasOwnProperty("after")) {
            if (!funcsObj["after"].hasOwnProperty("args")) {
                funcsObj["after"]["args"] = [];
            }
            funcsObj["after"]["func"].apply(this, funcsObj["after"]["args"]);
        }
    });

}

function remIptFl() {

    // NOTE: Remove the input file. By Questor
    $("#ipt_fl_parent").empty();

}

function addIptFl() {

    function addBefore(someArgs0, someArgs1) {
    // NOTE: All the logic here happens just before the file selection box opens.
    // By Questor

        // SOME CODE HERE!

    }

    function addAfter(someArgs0, someArgs1) {
    // NOTE: All the logic here happens only if the user adds a file. By Questor

        // SOME CODE HERE!

        $("#ipt_fl_parent").prepend(jqElInst);

    }

    // NOTE: The input file is hidden as all manipulation must be done via js.
    // By Questor
    var jqElInst = $('\
<span>\
    <button type="button" onclick="remIptFl();">REMOVE INPUT FILE!</button>\
    <input type="file" name="input_fl_nm" style="display: block;">\
</span>\
');

    var funcsObj = {
        before: {
            func: addBefore,
            args: [someArgs0, someArgs1]
        },
        after: {
            func: addAfter,

            // NOTE: The instance with the input file ("jqElInst") could be passed
            // here instead of using the context of the "addIptFl()" function. That
            // way "addBefore()" and "addAfter()" will not need to be inside "addIptFl()",
            // for example. By Questor
            args: [someArgs0, someArgs1]

        }
    };
    dynIptFl(jqElInst, funcsObj);

}

谢谢! = D

答案 22 :(得分:0)

只需在类型为文件的输入上添加“更改”侦听器即可。即

<input type="file" id="file_to_upload" name="file_to_upload" />

我已经使用了jQuery,显然每个人都可以使用valina JS(根据要求)。

$("#file_to_upload").change(function() {

            if (this.files.length) {

            alert('file choosen');

            } else {

            alert('file NOT choosen');

            }

    });

答案 23 :(得分:0)

    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>

答案 24 :(得分:0)

//使用悬停而不是模糊

var fileInput = $("#fileInput");

if (fileInput.is(":hover") { 

    //open
} else {

}

答案 25 :(得分:0)

嗯,这并没有完全回答你的问题。我的假设是,你有一个场景,当你添加一个文件输入,并调用文件选择,如果用户命中取消,你只需删除输入。

如果是这种情况,那么:为什么要添加空文件输入?

动态创建一个,但只有在填充时才将其添加到DOM中。如下所示:

    var fileInput = $("<input type='file' name='files' style='display: none' />");
    fileInput.bind("change", function() {
        if (fileInput.val() !== null) {
            // if has value add it to DOM
            $("#files").append(fileInput);
        }
    }).click();

所以我在这里创建&lt; input type =&#34; file&#34; /&GT;在运行中,绑定到它的更改事件,然后立即调用单击。只有在用户选择文件并点击确定时才会触发更改,否则输入将不会添加到DOM,因此不会被提交。

这里的工作示例:https://jsfiddle.net/69g0Lxno/3/

答案 26 :(得分:0)

我的解决方案{@ 3}}充满了hacky,用于检测用户是否上传了文件,并且只有在他们上传文件时才允许他们继续。

基本上隐藏ContinueSaveProceed或任何按钮。然后在JavaScript中获取文件名。如果文件名没有值,则不要显示Continue按钮。如果它有值,则显示按钮。如果他们首先上传文件,然后他们尝试上传其他文件并单击取消,这也可以。

这是代码。

HTML:

<div class="container">
<div class="row">
    <input class="file-input" type="file" accept="image/*" name="fileUpload" id="fileUpload" capture="camera">
    <label for="fileUpload" id="file-upload-btn">Capture or Upload Photo</label>
</div>
<div class="row padding-top-two-em">
    <input class="btn btn-success hidden" id="accept-btn" type="submit" value="Accept & Continue"/>
    <button class="btn btn-danger">Back</button>
</div></div>

JavaScript:

$('#fileUpload').change(function () {
    var fileName = $('#fileUpload').val();
    if (fileName != "") {
        $('#file-upload-btn').html(fileName);
        $('#accept-btn').removeClass('hidden').addClass('show');
    } else {
        $('#file-upload-btn').html("Upload File");
        $('#accept-btn').addClass('hidden');
    }
});

CSS:

.file-input {
    width: 0.1px;
    height: 0.1px;
    opacity: 0;
    overflow: hidden;
    position: absolute;
    z-index: -1; 
}

.file-input + label {
    font-size: 1.25em;
    font-weight: normal;
    color: white;
    background-color: blue;
    display: inline-block;
    padding: 5px;
}

.file-input:focus + label,
.file-input + label:hover {
    background-color: red;
}

.file-input + label {
    cursor: pointer;
}

.file-input + label * {
    pointer-events: none;
}

对于CSS来说,其中很多是为了让每个人都可以访问网站和按钮。根据自己喜欢的方式设置按钮的样式。

答案 27 :(得分:0)

这是我的解决方案,使用文件输入焦点(不使用任何计时器)

&#13;
&#13;
var fileInputSelectionInitiated = false;

function fileInputAnimationStart() {
    fileInputSelectionInitiated = true;
    if (!$("#image-selector-area-icon").hasClass("fa-spin"))
        $("#image-selector-area-icon").addClass("fa-spin");
    if (!$("#image-selector-button-icon").hasClass("fa-spin"))
        $("#image-selector-button-icon").addClass("fa-spin");
}

function fileInputAnimationStop() {
    fileInputSelectionInitiated = false;
    if ($("#image-selector-area-icon").hasClass("fa-spin"))
        $("#image-selector-area-icon").removeClass("fa-spin");
    if ($("#image-selector-button-icon").hasClass("fa-spin"))
        $("#image-selector-button-icon").removeClass("fa-spin");
}

$("#image-selector-area-wrapper").click(function (e) {
    $("#fileinput").focus();
    $("#fileinput").click();
});

$("#preview-image-wrapper").click(function (e) {
    $("#fileinput").focus();
    $("#fileinput").click();
});

$("#fileinput").click(function (e) {
    fileInputAnimationStart();
});

$("#fileinput").focus(function (e) {
    fileInputAnimationStop();
});

$("#fileinput").change(function(e) {
    // ...
}
&#13;
&#13;
&#13;

答案 28 :(得分:0)

有一种做法的hackish方式(添加回调或解决一些延迟/承诺实现而不是alert()次调用):

var result = null;

$('<input type="file" />')
    .on('change', function () {
        result = this.files[0];
        alert('selected!');
    })
    .click();

setTimeout(function () {
    $(document).one('mousemove', function () {
        if (!result) {
            alert('cancelled');
        }
    });
}, 1000);

工作原理:文件选择对话框打开时,文档不会接收鼠标指针事件。允许对话框实际出现并阻止浏览器窗口有1000毫秒的延迟。选中Chrome和Firefox(仅限Windows)。

但是,这当然不是检测取消对话框的可靠方法。但是,可能会为您改善一些UI行为。

答案 29 :(得分:-1)

q=course

使用setTimeout获取输入的特定值。

答案 30 :(得分:-1)

我找到了这个属性,它最简单。

if ($('#selectedFile')[0].files.length > 1)
{
   // Clicked on 'open' with file
} else {
   // Clicked on 'cancel'
}

此处selectedFileinput type=file

答案 31 :(得分:-2)

您可以在输入字段上创建一个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
    }
});

答案 32 :(得分:-3)

输入类型=文件代码:

onchange="if(this.files[0]!=undefined){ UploadImg(); }else{ alert('cancel'); }"

答案 33 :(得分:-3)

我需要这样做,最后我创建了一个非常简单的解决方案。可以在这里看到: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);
    }

});