【问题标题】:onclick() and onblur() ordering issueonclick() 和 onblur() 排序问题
【发布时间】:2013-07-20 02:12:13
【问题描述】:

我有一个输入字段,它会显示一个自定义下拉菜单。我想要以下功能:

  • 当用户点击输入字段外的任意位置时,菜单应被移除。
  • 更具体地说,如果用户单击菜单内的 div,则应删除该菜单,并且应根据单击的 div 进行特殊处理。

这是我的实现:

输入字段有一个onblur() 事件,当用户在输入字段外单击时删除菜单(通过将其父级的innerHTML 设置为空字符串)。菜单中的 div 也有onclick() 事件执行特殊处理。

问题是onclick() 事件在单击菜单时永远不会触发,因为输入字段的onblur() 首先触发并删除菜单,包括onclick()s!

我通过将菜单 div 的 onclick() 拆分为 onmousedown()onmouseup() 事件并在鼠标按下时设置一个全局标志来解决问题,该标志在鼠标抬起时被清除,类似于 this answer 中的建议。因为onmousedown()onblur() 之前触发,所以如果单击了其中一个菜单div,则该标志将设置在onblur() 中,但如果屏幕上的其他位置被单击,则不会。如果菜单被点击,我会立即从onblur() 返回而不删除菜单,然后等待onclick() 触发,此时我可以安全地删除菜单。

有没有更优雅的解决方案?

代码如下所示:

<div class="menu" onmousedown="setFlag()" onmouseup="doProcessing()">...</div>
<input id="input" onblur="removeMenu()" ... />

var mouseflag;

function setFlag() {
    mouseflag = true;
}

function removeMenu() {
    if (!mouseflag) {
        document.getElementById('menu').innerHTML = '';
    }
}

function doProcessing(id, name) {
    mouseflag = false;
    ...
}

【问题讨论】:

    标签: javascript onclick onblur


    【解决方案1】:

    onClick 不应替换为 onMouseDown

    虽然这种方法有些工作,但两者是根本不同的事件,在用户眼中具有不同的期望。在这种情况下,使用onMouseDown 而不是onClick 会破坏软件的可预测性。因此,这两个事件是不可互换的。

    举例说明:当不小心点击一个按钮时,用户期望能够按住鼠标点击,将光标拖到元素外,然后松开鼠标按钮,最终导致没有动作。 onClick 这样做。 onMouseDown 不允许用户按住鼠标不放,而是会立即触发一个动作,对用户没有任何追索权。 onClick 是我们期望在计算机上触发操作的标准。

    在这种情况下,请在 onMouseDown 事件上调用 event.preventDefault()onMouseDown 默认会引发模糊事件,在调用preventDefault 时不会这样做。然后,onClick 将有机会被调用。模糊事件仍然会发生,只是在onClick 之后。

    毕竟,onClick 事件是 onMouseDownonMouseUp 的组合,当且仅当它们都出现在同一个元素中。

    【讨论】:

    • 这对我来说是完美的解决方案,评论完全正确 - 替换 onMouseDown 会混淆用户体验。投了赞成票。
    • 这应该是正确的答案。感谢您发布此内容
    • 值得注意的是,这也有一些轻微的副作用,例如。无法通过在元素内单击来选择文本。想不出有太多这​​样的问题,就是觉得有点好笑。
    • 如果我在onMouseDown 事件上使用event.preventDefault(),则不会调用我的onFocus 事件
    • 焦点位置在许多应用程序中都很重要。标记的解决方案不允许通过回调将焦点恢复到初始输入框。 onBlur 将发生并移除焦点。这个使用event.preventDefault() 的实现确实允许onClick 处理程序将焦点设置到文本框。
    【解决方案2】:

    我遇到了和你一样的问题,我的 UI 完全按照你的描述设计。我通过简单地将菜单项的onClick 替换为onMouseDown 解决了这个问题。我什么也没做;没有onMouseUp,没有标志。这解决了这个问题,让浏览器根据这些事件处理程序的优先级自动重新排序,而无需我做任何额外的工作。

    有什么理由说明这对你不起作用?

    【讨论】:

    • 这确实有效。我已经在 FF 中对其进行了测试,效果非常好。
    • 它有效。看起来 onmousedownonblur 之前执行,在 Firefox、Chrome 和 Internet Explorer 中测试。
    • 值得注意的是,这确实会自然地改变行为 - 然后在鼠标按下而不是鼠标向上时处理点击交互。对于大多数人来说可能很好(在这种情况下包括自我),但也有一些缺点。最值得注意的是,如果我单击错误,我经常单击然后拖动按钮,这会阻止调用 onclick - 如果按钮执行非纯功能(删除、发布等),您可能希望保留它并使用标志方法。
    • 在你设置 mouseDown 而不是 onClick 的那一刻,可访问性怎么样,你会杀死所有
    • @ian-macfarlane 下面列出的答案是处理此问题的正确方法。总之...onMouseDownevent.preventDefault()onClick 以及您想要的操作。
    【解决方案3】:

    通过 onfocus 改变 onclick

    即使onblur和onclick相处得不是很好,但显然onfocus和yes onblur。因为即使在菜单关闭后,onfocus 仍然对内部单击的元素有效。

    我做到了,它奏效了。

    【讨论】:

      【解决方案4】:

      您可以在 onBlur 处理程序中使用 setInterval 函数,如下所示:

      <input id="input" onblur="removeMenu()" ... />
      
      function removeMenu() {
          setInterval(function(){
              if (!mouseflag) {
                  document.getElementById('menu').innerHTML = '';
              }
          }, 0);
      }
      

      setInterval 函数将从调用堆栈中删除您的 onBlur 函数,添加因为您将时间设置为 0,此函数将在其他事件处理程序完成后立即调用

      【讨论】:

      • setInterval 是个坏主意,您永远不会取消它,因此它将不断运行您的功能,并且很可能导致您的网站使用最大 cpu。如果您打算使用这种技术(我不推荐),请改用setTimeout。让您的应用依赖于某些事件的发生时间是有风险的事情。
      • 鲁滨逊会有危险!危险!未来的比赛条件!
      • 我最初想出了这个解决方案(好吧setTimeout 不是setInterval),但我不得不将它提高到250ms,然后才能以正确的顺序发生。这个错误可能仍然存在于速度较慢的设备上,并且它也会使延迟明显。我尝试了onMouseDown,它工作正常。我想知道它是否也需要触摸事件。
      • 如果你真的想使用onClick,像间隔这样的东西也不错。但是你想要一个带有条件而不是间隔的递归超时,这样它就不会永远运行。这与 Selenium 条件等待使用的模式相同。
      【解决方案5】:

      onmousedown 替换为onfocus。所以当焦点在文本框内时会触发这个事件。

      onmouseup 替换为onblur。当您将焦点移出文本框时,onblur 就会执行。

      我想这就是你可能需要的。

      更新

      当你在 onfocus 上执行你的函数时——>移除你将在 onblur 中应用的类并添加你想要在 onfocus 上执行的类

      当你执行你的函数 onblur-->删除你将在 onfocus 中应用的类 并添加要在 onblur 上执行的类

      我认为不需要标志变量。

      更新 2:

      您可以使用事件 onmouseout 和 onmouseover

      onmouseover - 检测光标何时在其上方。

      onmouseout-检测光标何时离开。

      【讨论】:

      • 为了清楚起见,我已经编辑了我的问题。该解决方案如何避免设置标志的需要?另外,我在菜单上使用onmousedown()onmouseup(),而不是文本框/输入字段。
      • 我还不能接受这个答案,因为我不知道它是否正确。您能否回应我在上述评论中提出的担忧?
      • 当然,我可以看到你如何使用 onmouseover 和 onmouseout 类似于我目前正在做的事情,当鼠标悬停在菜单上时设置一个标志。但是,我仍然认为您需要在 onmouseover 中设置一个标志,该标志在 onmouseout 中被清除,以便您在单击时知道您是否在菜单上方。你能想出一种不需要设置标志的方法吗?
      • 我能看到你的代码吗...把它放在小提琴里...只要把相关的部分和它完全可以,如果我没有看到结果..只是代码..
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-10
      • 1970-01-01
      • 2010-11-14
      • 2020-02-19
      • 2018-04-18
      • 2012-06-14
      • 2010-11-05
      相关资源
      最近更新 更多