我已经实现了一个照片上传功能到我的个人项目。
这段代码是使用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文件的回答)。
我对这个问题的解决方法如下:
input
元素不应该在mat-menu
中。也就是说,它可以在任何地方,除非你不能保证该元素每次都可用。因此,我把这个元素移到了HTML代码中的另一个位置,在那里我100%确定它在元素树中100%的时间都可用。
1.样式化按钮仍然调用#file
引用和click()
方法,因此流程按预期继续。
1.文件由用户选择。
1.文件上传成功,因为目标在元素树中可用,因此不为空。
问题解决了!
1条答案
按热度按时间sycxhyv71#
在事件激发时,您的输入可能未出现在元素树中。通过以编程方式创建输入、设置其类型并调用click方法,可以在空白页上重复此操作。
如果此时元素树中仍然存在输入,但是
target = null
中仍然有来自它的事件,那么这就很奇怪了。您可能在代码中安装了库,或者浏览器扩展影响了事件的工作方式。请在about:blank
页面上以隐身模式检查它。您也可以尝试重新启动Angular服务器。如果找不到问题的根源,我可以建议一个解决方法:不要将
$event
变量传递给savePhoto
方法,而是将file
传递给savePhoto
方法,这样就可以从中获得所需的所有信息。