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

Ppp*_*Ppp 97 html file input

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

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

Shi*_*boe 43

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

document.body.onfocus = function(){ /*rock it*/ }
Run Code Online (Sandbox Code Playgroud)

有什么好处的,就是你可以及时附加/分离它与文件事件,它似乎也可以正常使用隐藏的输入(如果你使用一个视觉解决方法的蹩脚的默认输入类型='一个明确的振作'文件').之后,您只需要确定输入值是否已更改.

一个例子:

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')
}
Run Code Online (Sandbox Code Playgroud)

看到它在行动:http://jsfiddle.net/Shiboe/yuK3r/6/

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

  • 这对我来说在Firefox和Edge中不起作用.然而,在`window.document`上添加`来监听`mousemove`事件,以及在`window`上听'焦点'似乎无处不在(至少在现代浏览器中,我不关心过去十年的残骸).基本上,任何交互事件都可用于此任务,该任务被打开的文件打开对话框阻止. (3认同)
  • @JohnWeisz 你的评论应该是*THE*答案。天才。 (3认同)

Ode*_*ded 19

你不能.

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

  • "文件对话框的结果不会暴露给浏览器." 如果通过选择文件关闭对话框,则不会出现这种情况. (6认同)
  • @Ppp - 不。浏览器不会告诉你。 (2认同)
  • 使用文件对话框触发的_change_公开给浏览器。看,如果没有选择文件,并且此后也没有选择文件,则什么都没有改变,因此不会调度`change`事件。 (2认同)
  • 此外,如果已经选择了一个文件,并且您再次选择了同一个文件,则也不会为此调度 `change` 事件。 (2认同)

han*_*aad 9

新的文件系统访问 API将使我们的生活再次变得轻松:)

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

浏览器支持非常有限(2021 年 1 月)。该示例代码在 Chrome 桌面 86 中运行良好。


小智 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 */
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 你有没有检查过`else`语句是否得到评估? (5认同)
  • 我可以说,在进行一些跨浏览器测试之后,Firefox不会在cancel时触发更改事件,如果重新打开并第二次单击cancel,它也不会清除所选的现有文件。奇怪吧 (3认同)

Dou*_*ker 8

因此,自从我提出了一个新颖的解决方案以来,我将对此表示怀疑。我有一个渐进式Web应用程序,允许用户捕获照片和视频并上传它们。我们会尽可能使用WebRTC,但对于支持较少的设备,请使用HTML5文件选择器*咳嗽Safari咳嗽*。如果您专门在使用本机相机直接捕获照片/视频的Android / iOS移动Web应用程序上工作,那么这是我遇到的最好的解决方案。

这个问题的症结在于,当页面加载时,fileis null,但是当用户打开对话框并按“ Cancel”时,fileis仍然是null,因此它没有“更改”,因此不会触发“更改”事件。对于台式机来说,这还不错,因为大多数台式机UI并不依赖于知道何时调用取消,但是带动相机捕获照片/视频的移动UI 非常依赖于何时按下取消。

我最初使用该document.body.onfocus事件来检测用户何时从文件选择器返回,并且该方法适用于大多数设备,但是iOS 11.3打破了该事件,因为未触发该事件。

概念

我的解决方案是* shudder *,以测量CPU时序以确定该页面当前处于前台还是后台。在移动设备上,将处理时间分配给当前位于前台的应用程序。当摄像机可见时,它将占用CPU时间并降低浏览器的优先级。我们需要做的就是测量给页面多少处理时间,当相机启动时,我们的可用时间将大大减少。关闭相机(取消或取消相机)后,我们的可用时间会增加。

实作

通过使用setTimeout()X毫秒来调用回调,然后测量实际调用它所花费的时间,我们可以测量CPU时间。浏览器永远不会在X毫秒之后精确地调用它,但是,如果可以合理地关闭它,那么我们必须处于前台。如果浏览器距离很远(比要求的速度慢10倍以上),那么我们必须在后台。这样的基本实现是这样的:

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);
  });
}
Run Code Online (Sandbox Code Playgroud)

我在最新版本的iOS和Android上对此进行了测试,通过在<input />元素上设置属性来调出本机相机。

<input type="file" accept="image/*" capture="camera" />
<input type="file" accept="video/*" capture="camcorder" />
Run Code Online (Sandbox Code Playgroud)

这实际上比我预期的要好得多。它通过请求在25毫秒内调用计时器来运行10次试用。然后,它测量实际调用所需的时间,如果10次尝试的平均时间少于50毫秒,则我们假设必须处于前台并且摄像机已关闭。如果大于50毫秒,那么我们必须仍然在后台并且应该继续等待。

一些其他细节

我之所以使用setTimeout()它,不是setInterval()因为后者可以将多个调用排入队列,这些调用彼此之后立即执行。这可能会大大增加数据中的噪声,因此我坚持这样做,setTimeout()尽管这样做有点复杂。

这些特殊的数字对我来说效果很好,尽管我至少看到过一次相机过早熄灭的情况。我认为这是因为相机打开速度可能很慢,并且该设备可能在实际变为背景之前进行了10次试用。添加更多试用版或等待25至50毫秒才能启动此功能,可能是一种解决方法。

桌面

不幸的是,这不适用于桌面浏览器。从理论上讲,相同的技巧是可能的,因为它们确实将当前页面放在优先于背景页面的位置。但是,即使在后台运行,许多台式机也有足够的资源来使页面保持全速运行,因此这种策略实际上并没有真正起作用。

替代解决方案

很少有人提到我确实探索过的另一种解决方案是模拟FileList。我们开始null<input />后,如果用户打开摄像头,并取消他们回来null,这是不发生变化,没有事件将触发。一种解决方案是将一个虚拟文件分配给<input />页面开始处,因此将设置为null会触发适当事件的更改。

不幸的是,没有正式的方法来创建FileList,并且<input />元素FileList特别需要a ,并且除了以外将不接受任何其他值null。自然地,FileList对象不能直接构造,可以解决一些旧的安全问题,这些问题显然不再相关。保持<input />元素外部唯一的方法是利用一种黑客技术,该技术通过复制粘贴数据来伪造一个可能包含FileList对象的剪贴板事件(您基本上是在伪造一个拖放文件, -您的网站事件)。这在Firefox中是可行的,但对于iOS Safari则不可行,因此在我的特定用例中不可行。

浏览器,请...

不用说,这显然是荒谬的。网页的关键用户界面元素已更改为零通知这一事实简直是可笑的。这确实是规范中的错误,因为它原本不打算用于全屏媒体捕获UI,并且从技术上讲,不触发“ change”事件是技术上的规范。

但是,浏览器供应商能否认识到这一现实?这可以通过新的“完成”事件来解决,即使没有发生更改也可以触发该事件,或者无论如何您都可以触发“更改”。是的,这违背了规范,但是对于我而言,在JavaScript方面简化变更事件是微不足道的,但是从根本上来说,不可能发明自己的“完成”事件。如果不保证浏览器的状态,即使我的解决方案实际上也只是试探法。

就目前而言,此API从根本上讲不适用于移动设备,而且我认为相对简单的浏览器更改可能会使Web开发人员无限轻松地“摆脱肥皂盒”。


zvo*_*kov 6

当您选择一个文件并单击打开/取消时,该input元素应该失去焦点blur.假设初始valueinput是空的,在你的任何非空值的blur处理程序将指示OK,和一个空值将意味着取消.

更新:隐藏blur时不会触发input.所以不能使用这个技巧与基于IFRAME的上传,除非你想暂时显示input.

  • 在Firefox 10 beta和Chrome 16中,选择文件时不会触发"blur".没试过其他浏览器. (3认同)
  • 这是行不通的——点击后立即触发输入模糊。Win7 上的 Chrome 63.0.3239.84 x64。 (2认同)

AEQ*_*AEQ 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!');
    }

});
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到它:http://jsfiddle.net/T3Vwz

  • 我在 Chrome 51 上测试过,第一次选择文件时它不起作用。解决方法是添加延迟:http://jsfiddle.net/7jfbmunh/ (2认同)

小智 5

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

问题是,你永远不知道这将触发事件的拳头,是它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);
    };
});
Run Code Online (Sandbox Code Playgroud)