当HTML select元素关闭时,是否有一个触发DOM事件?

69
我正在寻找一个DOM事件,可以用JavaScript监听,当打开了选择元素(但未更改任何选项),然后通过单击选择元素之外的页面上的任何其他地方关闭时,触发该事件。
这不是选择框的模糊事件,因为选择框保留焦点。同样,这也不是其他元素或文档的焦点事件,或者窗口、文档或正文的mousedown或click事件。
这也不是选择框的更改事件,因为没有更改选择中的任何选项。
我不关心旧版Internet Explorer - 只需要在符合标准的现代浏览器中工作。专有的hack可能值得一试。
我创建了一个JSFiddle来演示问题:http://jsfiddle.net/premasagar/FpfnM/ 1. 在“结果”面板中单击选择框 2. 单击标记为“HERE”的文本(或任何其他地方)一次,并查看是否向日志中添加了任何事件。在最新版本的Chrome或Firefox中没有事件。
所以问题是:添加什么JavaScript代码,才能在单击选择框之外的位置时记录事件? (我在这里提出了类似但不同的问题:
JavaScript on iOS: opening an HTML select element

9
如果您解释一下为什么想这样做,可能会有所帮助。 - Pointy
2
因为对你的问题直接、简单的回答是“不”。 :-) - Pointy
1
原因在另一个问题的评论中: http://stackoverflow.com/questions/6097240/javascript-on-ios-opening-an-html-select-element#comment-7069149但原因很复杂。 简而言之,我只是想在选择元素状态改变时获取有关其状态的一些程序信息。 如果不可能,我真的很想知道为什么不行(?) - Prem
3
从阅读其他问题的内容来看,似乎您只需要实现自己的小部件,该小部件几乎像 <select> 一样运作。 - Pointy
3
可以,但感觉有点奇怪。我越深入研究选择元素的行为,它们就越像一个Flash嵌入式黑匣子。没有办法通过编程打开它们,也没有办法检查它们是否已经打开,更没有办法跟踪它们关闭的事件。随着Web应用程序的发展和HTML5的普及,对DOM中的组件有了更大的控制权,但是谦虚的选择器似乎仍然不可编写脚本。 - Prem
显示剩余5条评论
7个回答

40

很不幸,没有标准事件可以知道一个选择框是打开还是关闭,因此您的代码将需要适应不同浏览器,可能会比较复杂。话虽如此,我认为这是可以做到的,并且我已经在Firefox中使用mouseup事件为您实现了一些功能:

http://jsfiddle.net/FpfnM/50/

在Firefox(我假设Chrome也是如此),您需要仔细跟踪选择状态。当按下escape键或发生blur事件时,您需要更新状态以将其标识为关闭。我在演示中尚未实现此功能,如果您按下escape键而不是取消选择,则可以看到会发生什么。
在Safari中,情况更容易,因为对选择的mousedown事件表示打开选择,而选择的任何关闭都由click事件表示。
如果您找到一个浏览器,其中没有这些事件被触发,您可以尝试一种额外的技巧。因为表单小部件(例如选择和文本区域)通常由操作系统呈现而不是在浏览器内部呈现,所以可能会与它们交互,并且某些消息可能无法传递到浏览器的事件处理程序中。如果您在选择框打开时定位一个覆盖屏幕的透明文本区域,并将其放置在较低的z-index上,则可以使用它来跟踪关闭单击。它可能能够捕获浏览器DOM元素可能错过的事件。
更新:Chrome在打开选择时在选择上看到mousedown,在页面用选择打开时在文档上看到mouseup。以下是适用于Chrome的版本:

http://jsfiddle.net/FpfnM/51/

再次强调,您需要进行一些浏览器检测以处理每个浏览器的正确行为。特别是在Chrome中,有一个陷阱,就是当您第二次单击选择框以关闭它时,不会触发任何事件。但是,您可以检测页面点击事件。

快速摘要:

Chrome/Safari: select.mousedown on open, document.mouseup on close
Firefox: select.click on open, document.mouseup on close
IE8/IE7: select.click on open, document.mouseup on close

这里有很多其他事件的边缘情况,会表示关闭(例如按下escape键、失去焦点等),但这些是最小限度的处理方式,以应对用户点击选择框,然后点击到文档区域的情况。
希望这有所帮助!

谢谢。是的,在Chrome中,只有在选择的选项更改时才会收到已登录事件。如果我只是打开选择,然后通过单击“HERE”关闭它,我不会收到事件。 - Prem
@Premasager - 我已经更新了答案,并提供了一个Chrome示例,并在IE上测试了行为。 IE / Firefox似乎对打开和关闭处理程序都有类似的工作方式。 Chrome / Safari需要在打开时观察选择时的mousedown事件,并在文档上进行mouseup以进行关闭。 - Jason Striegel
2
那个 jsFiddle 只是切换“打开/关闭”消息,不能准确反映实际事件。(例如,只需单击并选择一个项目,然后再次执行。) - mix3d
在我的情况下,我通过添加一个监听器来捕获关闭事件,当鼠标离开时,像这样:document.getElementById('selectId').addEventListener('mouseleave', ($event) => { console.log($event); }); - Victor Oliveira
这在iOS Safari上不起作用。当它打开时,mousedownmouseup都会触发,但关闭时两者都不会触发。我尝试使用clickblur和其他方法来捕获关闭事件,但都没有触发。 - Alex

2
如果我们认为在选择框外单击可以作为选择事件的结束信号。
以下jQuery代码可以实现此功能。这里是fiddle中的代码。
$(function () {
    let flag = 0;

    $("#selectId").click(function (e) {
        e.stopPropagation();
        if (flag === 0) {
            flag = 1;
            $(document).one("click", function () {
                flag = 0;
                alert("select end");
            });
        }
    });
});

HTML代码:

<select multiple id="selectId">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="opel">Opel</option>
<option value="audi">Audi</option>


2
当您有多个下拉菜单时:
        $("select").each(function () {
        var initDropdown = $(this);
        initDropdown.data("open", false);
        console.log(this.name + "   closed");

        initDropdown.on("blur", function () {
            var blurDdopdown = $(this);
            blurDdopdown.data("open", false);
            console.log(this.name + "   closed");
        });
    });

    $("select").bind("click", function () {
        var clickedDropdown = $(this);

        if (clickedDropdown.data('open') == false) {
            clickedDropdown.data('open', true);
            console.log(this.name + "   open");
        } else {
            clickedDropdown.data('open', false);
            console.log(this.name + "   closed");
        }
    });

1
我按照您在 JSFiddle 上的指示一字不漏地操作,得到了以下事件:
BODY, mousedown, STRONG
#document, mousedown, STRONG
window, mousedown, STRONG
SELECT, blur, SELECT
BODY, click, STRONG
#document, click, STRONG
window, click, STRONG

这些都是在选择菜单已经聚焦和展开后,我点击“HERE”时触发的事件。这是在最新版本的Chrome中。

任何一个都应该足以满足您的目的,不是吗?

编辑:如果您想确保失去焦点的是您的Select元素,请设置一个全局Javascript变量“selectFocused”,并将其设置为False。当您的Select菜单获得焦点时,将其设置为True,并在上述任何事件发生时将其设置为False。“selectFocused”现在可以在代码的任何地方使用,以检测您的Select元素当前是否具有焦点,当它更改值时,您就知道您的Select元素已被选中或取消选中。


你是只点击了一次“HERE”还是两次?在最新的Chrome中,当我只点击一次“HERE”时,<select>不会失去焦点。它仍然保持着焦点。 - Prem
我也看到了这种行为:Chrome vs. 13.0.772.0 dev-m。 - squidbe
哦,我也在Linux上使用Chrome 13.0.772.0 dev。但是我只点击一次时并没有触发模糊事件,在Firefox 4.0.1上也是如此。 - Prem
在Windows上的Firefox 4.0.1中,这些事件出现之前需要再次单击HERE。第一次单击HERE时不会触发任何事件。 - Kevin
冰人,敌机已确认。我从Firefox中得不到任何输出,那个输出是来自Chrome的(尽管我只点击了一次,Premasagar)。楼主,这似乎是你应该提交给bugzilla的问题,因为Chrome和IE都像我上面说的那样行事,只有Firefox没有注册任何事件。不确定为什么Linux中的Chrome表现不同,真是让人沮丧。 :( - sichinumi

1
我的第一反应有点绕弯,它的实现方式是使用通常用于在点击外部时关闭(自定义)下拉菜单的代码:
function clickInBody(){
 functionToCallOnBlur();
}

function clickInBodyStop(event){
    event.stopPropagation();
}

然后在您的 body 标签上添加 onClick="clickInBody()",在您的选择项上添加 onClick="clickInBodyStop(event)"。这应该在每次单击页面时调用事件,但如果您单击选择标记,则会停止传播并不调用 functionToCallOnBlur()

但问题在于,正如JSFiddle代码片段所示,当用户单击打开的选择器之外时,body上没有触发click事件。 - Prem
我认为我找到了问题,正如jsfiddle和firebug所显示的那样,问题在于clickInBody未定义,因此这是jsfiddle的问题而不是代码的问题,它应该在本地工作... - Tomas Reimers
正如我所说,如果body元素从未触发“click”事件,那么您提出的解决方案是行不通的,而它似乎从未这样做。 - Prem
1
然后在整个body周围放置一个包装器div,并使其触发onclick事件。 - Tomas Reimers
我不明白。在 body 周围放一个 div?没有任何元素产生点击事件,所以我不知道这怎么能解决问题。 - Prem
显示剩余3条评论

1

前提条件:使用jQuery!这可能只能解决你问题的一部分,但如果你正在寻找一个事件来触发当你点击一个元素时,你可以使用一个覆盖层div。

当用户点击选择框时:

$('#mySelectBox').click(function () {
    var myOverlayDiv = $('<div id="overlayDiv" class="overlayDiv"></div>')
    .click(function () {
        // call your "select lost focus" function here
        // which should include $('#overlayDiv').remove() somewhere!
    })
    .show()
    .appendTo(document.body);
});
叠加层的样式:
.overlayDiv {
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
opacity:0;
z-index:1000;
}

您需要确保您的选择框菜单具有更高的Z-index,这样当用户点击菜单时,他们会得到菜单而不是覆盖层div :)


您还可以在此函数和“选择失焦”函数中添加一些简单的代码,以比较用户与之交互前后的选择值。这将让您看到该值是否发生了更改,或者该框只是被打开和关闭了。 - jbabey
我很确定这不会起作用。当通过单击选择框以外的区域关闭选择框时,<body>元素不会接收到“click”事件,因此我无法相信div元素会接收到。你试过并使其工作了吗?我会试一下。 - Prem
是的,我已经在一个使用了一些自定义下拉菜单的Web应用程序中使其正常工作。 - jbabey
我忘了一件事(在我的应用程序和答案中) - 当覆盖层div接收到点击事件时,请确保将其从DOM中删除,以便您不会每次都添加它们 :P - jbabey

1

答案是“是的”,有这样一个事件,它被称为blur事件。它非常通用,取决于HTML元素。

HTMLSelectElement的情况下,当您退出元素的focused状态时,即关闭它时,会调用模糊。

同样,focus也可以被视为HTMLSelectElement元素的“打开”状态的替代品。

以下是一个示例,当您关闭HTMLSelectElement元素时执行某些callback

var select= document.getElementById("mySelect");

function doSomethingOnBlur(event){
  console.log("Yey! The select options were closed.");
}

select.addEventListener("blur", doSomethingOnBlur);
<select id="mySelect">
  <option selected hidden></option>
  <option>Some Item</option>
</select>

需要注意的是,选项是根据选择状态呈现的。当你聚焦它时,它会打开。而当你失去聚焦失焦)时,它就关闭了。因此,从技术上讲,这只是一种措辞选择...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接