Javascript 拦截“Ctrl+O"不打开我的文件对话框

2022-01-15 00:00:00 dialog jquery javascript html

我的 HTML 中有一个 <input type="file" id="browse-button"/> 文件浏览器输入.

我有另一个 ID 为 choose-file-button 的按钮,单击该按钮时会调用 document.getElementById("browse-button").click();.单击此按钮时,它会正确单击 #browse-button 并打开文件对话框.

现在,我从 this answer 获取代码来拦截 Ctrl+O 按键和打开我的文件对话框,所以我有这个:

$(window).bind('keydown', function(e){if (e.ctrlKey || e.metaKey){开关 (String.fromCharCode(e.which).toLowerCase()){案例's':e.preventDefault();//这个问题无关紧要返回假;案例o":e.preventDefault();document.getElementById("选择文件按钮").click();返回假;}}返回真;});

如您所见,当我拦截 Ctrl+O 时,我点击了我的 #choose-file-button 按钮,该按钮调用了 document.getElementById("browser-button"); 在其 onclick 处理程序中.我在这个点击处理程序中放置了一个断点,当我按下 Ctrl+O 时,它确实到达了这个断点.但是,文件对话框永远不会出现.

通过调试发现,如果我在#choose-file-button click()行后面加上一个alert(...);,那么警告显示和正常页面打开文件"对话框显示(不是我的文件对话框).但是,如果我没有此警报,则根本不会显示任何内容.

这是一个错误吗?如何修复它并通过截获的 Ctrl+O 显示我的文件对话框?

我刚刚在 Chrome 中进行了测试,它运行良好.但是,它仍然无法在 Firefox 中运行.

解决方案

这里有一些浏览器安全魔法.当使用超时或间隔或我尝试的任何其他方法时,代码照常进行,但浏览器只是拒绝打开文件上传对话框.这可能是故意的,以阻止恶意 JS 在未经同意的情况下尝试抓取用户的文件.但是,如果您绑定到链接上的单击事件,则可以使用 jQuery 或常规 JS 完美运行.

正如怀疑的那样,大多数浏览器会根据事件的类型以及它是由用户创建还是以编程方式生成来跟踪事件是否可信.请参阅 this answer 了解完整详情.如您所见,由于键盘事件不在列表中,因此它们永远不能被信任.

测试 JSFiddle

<form action="#" method="post">

<input type="file" id="myfile" name="myfile"/><a href="#" id="mylink" accesskey="o">点我</a></div></表格>$("#mylink").click(function () {$("#myfile").click();});$(window).bind('keydown', function (e) {如果(e.ctrlKey || e.metaKey){switch (String.fromCharCode(e.which).toLowerCase()) {案例o":e.preventDefault();控制台.log("1a");$("#myfile").click();//警报(你好");控制台.log("1b");返回假;}}返回真;});

我认为这里只有两个选项,它们都是变通方法,而不是解决方案.

  • 一种是使用链接触发文件上传对话框,并要求人们使用 ALT+SHIFT+O 而不是 CTRL+O(因为我添加了一个 accesskey 属性到示例中的链接).
  • 另一种选择是为 拖放文件上传.

附录:我还尝试在 Firefox 中使用纯 JavaScript 来获取点击事件,并使用 isTrusted 属性检查它是否受信任.对于链接上的点击,它返回 true.但是,尝试在其他地方存储和重用该事件是行不通的,因为在您获得对它的引用时它已经被分派了.此外,毫不奇怪,创建一个新事件并尝试设置 isTrusted = true 也不起作用,因为它是只读的.

I have an <input type="file" id="browse-button"/> file-browser input in my HTML.

I have another button with ID choose-file-button that, when clicked, calls document.getElementById("browse-button").click();. When this button is clicked, it correctly clicks #browse-button and the file dialog opens.

Now, I took code from this answer to intercept a Ctrl+O keypress and open my file dialog, so I have this:

$(window).bind('keydown', function(e)
{
    if (e.ctrlKey || e.metaKey)
    {
        switch (String.fromCharCode(e.which).toLowerCase())
        {
            case 's':
                e.preventDefault();
                // doesn't matter for this question
                return false;
            case 'o':
                e.preventDefault();
                document.getElementById("choose-file-button").click();
                return false;
        }
    }
    return true;
});

As you can see, when I intercept Ctrl+O I click on my #choose-file-button button, which calls document.getElementById("browse-button"); in its onclick handler. I have put a breakpoint in this click handler, and when I press Ctrl+O it does arrive at this breakpoint. However, the file dialog never shows up.

Through debugging, I found out that if I put an alert(...); after the #choose-file-button click() line, then the alert shows up and the normal page "Open File" dialog shows up (not my file dialog). If I do not have this alert, however, nothing shows up at all.

Is this a bug? How can I fix it and make my file dialog show up via the intercepted Ctrl+O?

Edit: I just tested in Chrome, and it works perfectly. However, it still does not work in Firefox.

解决方案

There's some browser security magic going on here. When using timeouts or intervals or any other methods I try, the code carries on as normal but the browser simply refuses to open a file upload dialog. This is probably deliberate, to stop malicious JS from trying to grab users' files without consent. However, if you bind to a click event on a link, it works perfectly using jQuery or regular JS.

Edit: As suspected, most browsers keep track of whether an event is trusted or not based on the type of event and whether it was created by the user or generated programmatically. Se this answer for the full details. As you can see, since keyboard events aren't in the list, they can never be trusted.

Test JSFiddle

<form action="#" method="post">
    <div>
        <input type="file" id="myfile" name="myfile" /> <a href="#" id="mylink" accesskey="o">Click me</a>
    </div>
</form>

$("#mylink").click(function () {
    $("#myfile").click();
});

$(window).bind('keydown', function (e) {
    if (e.ctrlKey || e.metaKey) {
        switch (String.fromCharCode(e.which).toLowerCase()) {
            case 'o':
                e.preventDefault();
                console.log("1a");

                $("#myfile").click();
                //alert("hello");

                console.log("1b");
                return false;
        }
    }
    return true;
});

I think there are only two options here, and they're both workarounds, not solutions.

  • One is to use a link to trigger the file upload dialog, and ask people to use ALT+SHIFT+O instead of CTRL+O (because I added an accesskey attribute to the link in the example).
  • The other alternative is to use one of the new HTML5 JavaScript APIs for drag-drop file uploading.

Addendum: I also tried using pure JavaScript in Firefox to grab a click event and check to see if it's trusted using the isTrusted property. For the clicks on the link, it returned true. However, attempting to store and re-use the event elsewhere doesn't work, because it's already been dispatched by the time you get a reference to it. Also, unsurprisingly, creating a new event and attempting to set isTrusted = true doesn't work either since it's read-only.

相关文章