【问题标题】:How to hide a dropdown when the user clicks outside of it当用户在下拉列表之外单击时如何隐藏下拉列表
【发布时间】:2017-04-23 19:14:09
【问题描述】:

下面是我正在使用的输入下拉菜单的简化版本。

它所做的基本总结是:如果您专注于输入,则会出现一个下拉菜单。如果您单击下拉列表中的选项之一,该选项将填充输入并且下拉列表消失。这是使用onfocus 和我称为dropdown();undropdown(); 的函数实现的。

我处于两难境地,当有人点击其他地方时,我无法让下拉菜单消失。如果我使用onblur,它会成功隐藏下拉菜单,但如果您单击一个选项,它不会填充输入,这是因为onblur 函数首先运行,因此input(); 函数不会t 不运行,因为下拉菜单已被隐藏。

如果您将onclick 放在主体标签或其他父标签上,它会将onfocus 视为一次点击,在此运行dropdown(); 函数,然后立即运行undropdown(); 函数,因此下拉菜单从此不再出现功能重叠。

我希望能帮助我弄清楚如何对函数进行排序,以便它们以正确的顺序执行而不会相互重叠。

JSFiddle 可用here.

function input(pos) {
  var dropdown = document.getElementsByClassName('drop');
  var li = dropdown[0].getElementsByTagName("li");
  document.getElementsByTagName('input')[0].value = li[pos].innerHTML;
  undropdown(0);
}
function dropdown(pos) {
  document.getElementsByClassName('content')[pos].style.display = "block"
}
function undropdown(pos) {
  document.getElementsByClassName('content')[pos].style.display = "none";
}
.drop {
  position: relative;
  display: inline-block;
  vertical-align: top;
  overflow: visible;
}
.content {
  display: none;
  list-style-type: none;
  border: 1px solid #000;
  padding: 0;
  margin: 0;
  position: absolute;
  z-index: 2;
  width: 100%;
  max-height: 190px;
  overflow-y: scroll;
}
.content li {
  padding: 12px 16px;
  display: block;
  margin: 0;
}
<div class="drop">
  <input type="text" name="class" placeholder="Class" onfocus="dropdown(0)"/>
  <ul class="content">
    <li onclick="input(0)">Option 1</li>
    <li onclick="input(1)">Option 2</li>
    <li onclick="input(2)">Option 3</li>
    <li onclick="input(3)">Option 4</li>
  </ul>
</div>

PS:除了上述问题,我希望能提出修改建议,以便为这个问题获得一个更好的标题,以便遇到类似问题的人更容易找到它。

【问题讨论】:

  • 可能有助于重现 www.jsfiddle.net 中的问题(更容易使用并提出解决方案) - 标题可能很好,通用性足以解决许多问题,但描述你的问题很漂亮准确。
  • 您不能使用&lt;select&gt;的任何特殊原因?
  • 通常你捕获点击(或传播到)文档元素并在必要时关闭下拉菜单。

标签: javascript html css input drop-down-menu


【解决方案1】:

在这种情况下,在onblur 上,您可以调用一个函数,该函数在一个非常小的 setTimeout 之后几乎立即触发 undropdown(0);。像这样:

function set() {
  setTimeout(function(){ 
    undropdown(0);
  }, 100);
}

HTML

<input type="text" name="class" placeholder="Class" onfocus="dropdown(0)" onblur="set()" />

无需其他更改。

function input(pos) {
  var dropdown = document.getElementsByClassName('drop');
  var li = dropdown[0].getElementsByTagName("li");

  document.getElementsByTagName('input')[0].value = li[pos].innerHTML;
  undropdown(0);
}

function dropdown(pos) {
  document.getElementsByClassName('content')[pos].style.display= "block"
}

function undropdown(pos) {
  document.getElementsByClassName('content')[pos].style.display= "none";
}

function set() {
  setTimeout(function(){ 
    undropdown(0);
  }, 100);
}
        .drop {
            position: relative;
            display: inline-block;
            vertical-align:top;
            overflow: visible;
        }

        .content {
            display: none;
            list-style-type: none;
            border: 1px solid #000; 
            padding: 0;
            margin: 0;
            position: absolute;
            z-index: 2;
            width: 100%;
            max-height: 190px;
            overflow-y: scroll;
        }

        .content li {
            padding: 12px 16px;
            display: block;
            margin: 0;
        }
   <div class="drop">
        <input type="text" name="class" placeholder="Class" onfocus="dropdown(0)" onblur="set()" />
        <ul class="content">
            <li onclick="input(0)">Option 1</li>
            <li onclick="input(1)">Option 2</li>
            <li onclick="input(2)">Option 3</li>
            <li onclick="input(3)">Option 4</li>
        </ul>
    </div>

【讨论】:

  • 此解决方案的一个问题是,如果用户点击缓慢(mousedown + mouseup 需要超过 100 毫秒),则不会选择该选项。您可以尝试慢慢点击演示以查看它的实际效果。
【解决方案2】:

您可以使用tabindex 使下拉菜单具有焦点,并且在输入的blur 事件侦听器中,仅当焦点没有进入下拉菜单时才隐藏下拉菜单(请参阅When onblur occurs, how can I find out which element focus went to?

<ul class="content" tabindex="-1"></ul>
input.addEventListener('blur', function(e) {
  if(!e.relatedTarget || !e.relatedTarget.classList.contains('content')) {
    undropdown(0);
  }
});

function input(e) {
  var dropdown = document.getElementsByClassName('drop');
  var li = dropdown[0].getElementsByTagName("li");
  document.getElementsByTagName('input')[0].value = e.target.textContent;
  undropdown(0);
}
[].forEach.call(document.getElementsByTagName('li'), function(el) {
  el.addEventListener('click', input);
});
function dropdown(pos) {
  document.getElementsByClassName('content')[pos].style.display = "block"
}
function undropdown(pos) {
  document.getElementsByClassName('content')[pos].style.display = "none";
}
var input = document.getElementsByTagName('input')[0];
input.addEventListener('focus', function(e) {
  dropdown(0);
});
input.addEventListener('blur', function(e) {
  if(!e.relatedTarget || !e.relatedTarget.classList.contains('content')) {
    undropdown(0);
  }
});
.drop {
  position: relative;
  display: inline-block;
  vertical-align: top;
  overflow: visible;
}
.content {
  display: none;
  list-style-type: none;
  border: 1px solid #000;
  padding: 0;
  margin: 0;
  position: absolute;
  z-index: 2;
  width: 100%;
  max-height: 190px;
  overflow-y: scroll;
  outline: none;
}
.content li {
  padding: 12px 16px;
  display: block;
  margin: 0;
}
<div class="drop">
  <input type="text" name="class" placeholder="Class" />
  <ul class="content" tabindex="-1">
    <li>Option 1</li>
    <li>Option 2</li>
    <li>Option 3</li>
    <li>Option 4</li>
  </ul>
</div>

【讨论】:

    【解决方案3】:

    接受的答案是一种幼稚且不可靠的方法。我在一个复杂的应用程序中遇到了一个难以捕捉的错误,因为我使用了 setTimeout 来提供大约 200 毫秒的延迟,以便浏览器可以在模糊事件发生之前处理下拉点击。虽然它在我测试的每个设置上都运行良好,但有些用户确实遇到了问题,尤其是机器速度较慢的用户。

    正确的方法是在focusout事件上测试relatedTarget

    input.addEventListener('focusout', function(event) {
      if(!isDropdownElement(event.relatedTarget)) {
        // hide dropdown
      }
    });
    

    relatedTarget for focusout 包含一个 element reference,它正在接收焦点。这在我迄今为止测试过的所有浏览器中都能可靠地工作(我没有测试 IE10 及更低版本,只测试了 IE11 和 Edge)。

    【讨论】:

      【解决方案4】:

      W3Schools 有一个很好的示例来说明如何创建自定义下拉菜单: https://www.w3schools.com/howto/howto_custom_select.asp

      这是该示例中处理焦点的方式:

      全局点击处理程序处理下拉列表之外的点击:document.addEventListener("click", closeAllSelect);。因此,只要用户单击文档中的任何位置,所有下拉菜单都会关闭。

      但是当用户选择下拉列表的一个元素时,点击事件会被选择处理程序内的e.stopPropagation(); 停止。

      这样,您不需要计时器解决方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-24
        • 2019-02-09
        • 2010-10-12
        • 1970-01-01
        • 2019-01-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多