【问题标题】:Is there a DOM event that fires when an HTML select element is closed?关闭 HTML 选择元素时是否会触发 DOM 事件?
【发布时间】:2011-09-06 16:00:46
【问题描述】:

我正在寻找一个 DOM 事件,我可以使用 JavaScript 来监听一个已打开的选择元素(但未更改选项),然后通过单击选择元素,在其他地方(任何地方)关闭选择元素页面。

这不是选择的blur 事件,因为选择保留焦点。同样,它不是某个其他元素或文档的focus 事件,也不是窗口、文档或正文上的mousedownclick

这不是 select 的 change 事件,因为 select 中的任何选项都没有更改。

我不关心旧的 Internet Explorer - 只是在符合标准的现代浏览器中工作的东西。不过,专有的 hack 可能值得了解。

我创建了一个 JSFiddle 来演示这个问题:http://jsfiddle.net/premasagar/FpfnM/

  1. 点击“结果”面板中的选择框
  2. 单击标记为“此处”的文本(或其他任何位置),然后查看是否有任何事件添加到日志中。最新的 Chrome 或 Firefox 中没有事件。

所以问题是:可以添加什么 JavaScript,以便在单击选择框时记录事件?

(我在这里问了一个类似但不同的问题:
JavaScript on iOS: opening an HTML select element

【问题讨论】:

  • 如果您解释了为什么要这样做,它可能会有所帮助。
  • ... 因为对您的问题的直接、简单的回答是“不”。 :-)
  • 原因在对另一个问题的评论中:stackoverflow.com/questions/6097240/… 但这是一个令人费解的原因。简而言之,我只是想在选择元素更改时获取一些有关选择元素状态的编程信息。如果不可能,我真的很好奇为什么不(?)
  • 通过阅读其他问题,在我看来,您只需要实现自己的小部件,它或多或少就像<select>
  • 还不错,虽然感觉很奇怪。我对选择元素的行为研究得越多,就越觉得它们是一个像 Flash embed 一样的黑盒子。没有办法以编程方式打开它们,没有办法检查它们是否打开,也没有办法跟踪它们关闭的事件。随着 HTML5 等 Web 应用程序的成熟,对 DOM 中的组件的控制比以往任何时候都更加强大,但不起眼的选择似乎在很大程度上无法编写脚本。

标签: javascript jquery html dom


【解决方案1】:

不幸的是,没有标准事件可以知道选择框何时关闭或打开,因此您的代码会因适应不同的浏览器而变得非常麻烦。也就是说,我认为它可以完成,并且我已经使用 mouseup 事件在 Firefox 中为您提供了一些工作:

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

在 Firefox(我假设是 Chrome)中,您需要非常仔细地跟踪该选择的状态。当按下escape 键或发生blur 事件时,您需要更新状态以将其标识为已关闭。我没有在我的演示中实现它,你可以看到如果你点击转义而不是点击选择会发生什么。

在 Safari 中事情变得更简单了,选择上的 mousedown 事件表示打开选择,而任何关闭选择都由 click 事件表示。

如果您发现浏览器没有触发这些事件,您可以尝试一个额外的技巧。因为像 select 和 textarea 这样的表单小部件通常由操作系统呈现,而不是在浏览器内部,所以您可能会与它们进行交互,并且某些消息可能不会到达浏览器的事件处理程序。如果您在选择框打开时将覆盖屏幕的透明文本区域放置在较低的 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

对于表示关闭的其他事件(退出按键、模糊等),有很多极端情况,但这些是处理用户单击选择框然后单击关闭的场景的最低限度文档区域。

希望这会有所帮助!

【讨论】:

  • 谢谢。是的,在 Chrome 中,如果选择的选项已更改,我只会收到记录的事件。如果我只是打开选择然后单击“此处”将其关闭,我不会收到事件。
  • @Premasager - 我已经用 Chrome 示例更新了答案,并在 IE 上测试了该行为。 IE/Firefox 似乎对 open 和 close 处理程序的工作方式相似。 Chrome/Safari 需要在选择打开时注意 mousedown,并在文档上注意 mouseup 是否关闭。
  • 那个 jsFiddle 只切换“打开/关闭”消息,并不能准确反映实际事件。 (例如,只需单击并选择一个项目,然后再做一次。)
  • 在我的情况下,当鼠标离开时,我捕获了关闭事件,添加了一个侦听器,如下所示:document.getElementById('selectId').addEventListener('mouseleave', ($event) => { console.log($event); });
  • 这不适用于 iOS Safari。 mousedownmouseup 在打开时都会触发,但在关闭时都不会触发。我试过clickblur和其他人来捕捉近距离,但没有一个开火。
【解决方案2】:

当您有超过 1 个下拉菜单时:

        $("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");
        }
    });

【讨论】:

    【解决方案3】:

    如果我们考虑在选择框外点击可能是结束选择事件的信号。

    下面的 jQuery 可以做到这一点。 Here 是小提琴中的代码

    $(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>
    

    【讨论】:

      【解决方案4】:

      按照您的 JSFiddle 上的指示,我得到了以下事件:

      BODY, mousedown, STRONG
      #document, mousedown, STRONG
      window, mousedown, STRONG
      SELECT, blur, SELECT
      BODY, click, STRONG
      #document, click, STRONG
      window, click, STRONG
      

      这些都是在选择菜单已经成为焦点并展开后单击“此处”时触发的所有事件。这是在最新版本的 Chrome 中。

      这些中的任何一个都不应该满足您的目的吗?

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

      【讨论】:

      • 您是否只点击了一次“这里”?还是两次?在最新的 Chrome 中,当我只单击“这里”一次时,
      • 我也看到了这种行为:Chrome vs. 13.0.772.0 dev-m
      • 嗯。我也在 Chrome 13.0.772.0 dev 上(在 Linux 上)。当我只单击一次或在 Firefox 4.0.1 中单击时,我没有得到模糊事件。
      • 在 Windows 上的 Firefox 4.0.1 上,在这些事件出现之前需要再次点击这里。第一次点击 HERE 时不会触发任何事件。
      • 冰人,柏忌被确认。我从 Firefox 中一无所获,该输出来自 Chrome(尽管我只单击了一次,Premasagar)。 OP,这似乎是你应该提交给 bugzilla 的东西,因为 Chrome 和 IE 的行为都像我上面所说的那样,只有 Firefox 不注册任何事件。不过,不确定为什么 Linux 中的 Chrome 表现不一样。 :(
      【解决方案5】:

      我的第一直觉是实现这一点的方式有点迂回,这将是使用您通常用于在外部按下时关闭(自定义)下拉菜单的代码:

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

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

      【讨论】:

      • 但问题是,正如 JSFiddle sn-p 所示,当用户单击打开的选择时,主体上没有触发 click 事件。
      • 我想我找到了问题,如thisjsfiddle和firebug所示,问题是clickInBody没有定义,所以这是jsfiddle的问题而不是代码,它应该在本地工作......
      • 正如我所说,如果 body 元素从不触发 click 事件,您提出的解决方案将无法工作,而它似乎从未触发过。
      • 然后在整个主体周围放置一个包装器 div 并触发onclick 事件。
      • 我不明白。在身体周围放一个div?任何元素都没有产生点击事件,所以我看不出这如何解决问题。
      【解决方案6】:

      前提条件:使用 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);
      });
      

      叠加 div 的样式:

      .overlayDiv {
      position:absolute;
      top:0;
      left:0;
      width:100%;
      height:100%;
      opacity:0;
      z-index:1000;
      }
      

      您需要确保您的选择框菜单的 Z-index 较高,因此当用户单击菜单时,他们会得到菜单而不是覆盖 div :)

      【讨论】:

      • 您也可以在这个函数和“选择失去焦点”函数中添加一些简单的代码来比较用户交互之前和之后的选择值。这将允许您查看值是否已更改,或者该框是否仅打开和关闭。
      • 我很确定这行不通。当通过单击选择框关闭选择时, 元素没有收到“点击”事件,所以我不太相信 div 元素会。你有没有试过并让它工作。我试试看。
      • 是的,我让它在一个使用一些自定义下拉菜单的网络应用程序中工作。
      • 我忘记了一件事(在我的应用程序和答案中) - 当覆盖 div 收到点击事件时,请确保将其从 DOM 中删除,这样您就不会每次都添加它们:P
      【解决方案7】:

      答案是“是”,有这样一个事件,称为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>

      请务必注意,选项是根据选择的状态呈现的。当您在上面focus 时,它会打开。当您输掉focus (blur) 时,它就会关闭。所以从技术上讲,这只是一个词的选择......

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-07
        • 2023-04-06
        • 2010-09-19
        • 1970-01-01
        • 2021-06-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多