event.target HTML输入文件类型和Angular 更改时,Chrome浏览器上的www.example.com始终为空

e37o9pze  于 2023-03-21  发布在  Go
关注(0)|答案(1)|浏览(142)

我已经实现了一个照片上传功能到我的个人项目。
这段代码是使用HTML/JavaScript处理文件上传的常用代码。我基本上要做的是:
1.使用<input type="file" />元素选择文件,并在(change)上将事件传递给Angular函数:

<input type="file" accept="image/*" capture size="60" #file (change)="savePhoto($event)" />

使用CSS的display: none;设置隐藏了input元素,因此用户只能看到样式正确的按钮。
有一个Angular 参考(#file),所以我可以从一个适当样式的按钮“单击”文件输入:

<button mat-menu-item (click)="file.click()">
    <mat-icon>camera</mat-icon>
    <span>Add Photo</span>
</button>

1.在函数中,选择event.target.files下的第一个文件(基本上是event.target.files[0]):

const file = event.target.files[0];

1.获得文件后,我可以根据需要迭代它并继续上传(为了简单起见,恢复到file.type):

const type = file.type;

这种方式在大多数浏览器上都能完美运行,但在Chrome上就不是这样了。
看,当在Chrome上使用完全相同的代码时,event.target总是null,因此event.target.files不存在。这是我得到的错误:
ERROR TypeError: Cannot read properties of null (reading 'files')
除了这个错误之外,任何迭代文件变量的代码都以这个错误结束(正如预期的那样,因为event.target为空,因此event.target.files也是未定义的):

ERROR Error: Uncaught (in promise): TypeError: Cannot read properties of undefined (reading 'type')
TypeError: Cannot read properties of undefined (reading 'type')

考虑到这一点,我已经将事件输出记录到Chrome中,这是Chrome下的event.target日志,请注意target为空:

{isTrusted: true, type: 'change', target: null, currentTarget: null, eventPhase: 0, …}

这种情况在Firefox 110.0.1(macOS)和Safari(最新的iOS/iPadOS/macOS)上都没有发生。我不知道我是否错过了什么,或者不同浏览器之间的事件行为是否不同。
如果event.target不能立即使用,则尝试应用超时(尽管实际上它没有做太多事情):

if (event.target === null) {
   setTimeout(() => {
      console.log(event);
      file = event.target.files.item(0);
   }, 0)
} else {
   file = event.target.files[0];
}

输出完全相同:

{isTrusted: true, type: 'change', target: null, currentTarget: null, eventPhase: 0, …}

还更改了检查event.target是否为空的函数,但没有用,因为在Chrome上,event.target由于某种原因总是为空,所以代码甚至不在这个阶段运行。
编辑:感谢Markiewic的回答,我终于能够理解这个问题并解决这个问题。我想分享更多的上下文,因为我确实错过了导致这个bug的一些有趣的地方。
我错误地将input标签插入到mat-menu组件中,这意味着input标签在元素树中并不总是可用的,总之,当菜单为open时,它是可用的。但是当菜单是closed时会被删除。这在Firefox和Safari下不是问题(以及许多其他非基于Chromium的浏览器),但基于Chromium的浏览器有一个问题(参见Markiewic对Chromium下相应bug文件的回答)。
我对这个问题的解决方法如下:

  1. input元素不应该在mat-menu中。也就是说,它可以在任何地方,除非你不能保证该元素每次都可用。因此,我把这个元素移到了HTML代码中的另一个位置,在那里我100%确定它在元素树中100%的时间都可用。
    1.样式化按钮仍然调用#file引用和click()方法,因此流程按预期继续。
    1.文件由用户选择。
    1.文件上传成功,因为目标在元素树中可用,因此不为空。
    问题解决了!
sycxhyv7

sycxhyv71#

在事件激发时,您的输入可能未出现在元素树中。通过以编程方式创建输入、设置其类型并调用click方法,可以在空白页上重复此操作。
如果此时元素树中仍然存在输入,但是target = null中仍然有来自它的事件,那么这就很奇怪了。您可能在代码中安装了库,或者浏览器扩展影响了事件的工作方式。请在about:blank页面上以隐身模式检查它。您也可以尝试重新启动Angular服务器。
如果找不到问题的根源,我可以建议一个解决方法:不要将$event变量传递给savePhoto方法,而是将file传递给savePhoto方法,这样就可以从中获得所需的所有信息。

<input type="file" ... #file (change)="savePhoto(file)" />

相关问题