onclick()和onblur()排序问题


102

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

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

这是我的实现:

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

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

我通过将菜单div onclick()划分为onmousedown()onmouseup()事件并在鼠标按下时设置了全局标记(在鼠标按下时清除)来解决了该问题,类似于此答案中的建议。因为onmousedown()在之前触发onblur(),所以onblur()如果单击菜单div之一将在该标志中设置,但是如果单击屏幕上的其他位置则不会设置该标志。如果单击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;
    ...
}

Answers:


168

我遇到了与您完全相同的问题,我的UI的设计完全符合您的描述。我通过简单地用替换onClick菜单项的解决了这个问题onMouseDown。我什么也没做;不onMouseUp,没有标志。通过让浏览器根据这些事件处理程序的优先级自动重新排序,从而解决了问题,而无需我做任何其他工作。

有什么理由为什么这对您也不会起作用?


3
这实际上有效。我已经在FF中对其进行了测试,它的工作原理就像魅力一样。
至尊海豚

3
有用。它看起来像在Firefox,Chrome和Internet Explorer中经过测试onmousedown之前执行的onblur
Tonatio

11
值得一提的是,这确实会自然改变行为-单击交互是在鼠标按下而不是鼠标按下时进行的。对于大多数人来说可能还不错(在这种情况下自己包括在内),但有一些缺点。最值得注意的是,如果我单击不正确,我经常单击然后将其拖出按钮,这可以防止调用onclick-如果按钮执行非纯函数(删除,发布等),则可能需要保留此标志并采用标记方法。
Brizee '17

2
在您将mouseDown设置为onClick时,可访问性如何?您杀死所有<button>元素的可访问性:/
ncubica

2
@ ian-macfarlane在下面列出的答案是处理此问题的正确方法。在总结... onMouseDownevent.preventDefault()onClick与您期望的动作。
锥形三硝基甲苯达科斯塔

47

onClick不应替换为onMouseDown

尽管这种方法有些奏效,但两者在本质上是不同的事件,在用户眼中具有不同的期望。在这种情况下,使用onMouseDown代替onClick将会破坏软件的可预测性。因此,这两个事件是不可互换的。

举例说明:意外单击按钮时,用户希望能够按住鼠标单击,将光标拖到元素外,然后释放鼠标按钮,最终不会采取任何措施。onClick做这个。onMouseDown不允许用户按住鼠标,而是立即触发操作,而无需用户追索。onClick是我们期望在计算机上触发操作的标准。

在这种情况下,调用event.preventDefault()onMouseDown事件。onMouseDown默认情况下会导致模糊事件,而在preventDefault调用时不会这样做。然后,onClick将有机会被称为。仅在之后,模糊事件仍然会发生onClick

毕竟,onClick事件的组合onMouseDownonMouseUp,当且仅当它们都发生在同一单元内。


7
这对我来说是一个完美的解决方案,注释是完全正确的-替换onMouseDown会使用户体验混乱。投票了。
Paul Bartlett

5
这应该是正确的答案。感谢您发布此内容
user1189352

1
值得注意的是,这确实也有一些小的副作用,例如,无法通过在元素内单击来选择文本。想不到太多情况会成为问题,只是觉得有点可笑。
宫坂丽

1
如果我event.preventDefault()onMouseDown事件上使用事件,onFocus则不会调用我的事件
Batman

4

替换onmousedownonfocus。因此,当焦点位于文本框内时将触发此事件。

替换onmouseuponblur。当您从文本框中移出焦点时,onblur将执行。

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

更新

当您执行功能onfocus时->删除将在onblur中应用的类,并添加要在onfocus中执行的类

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

我认为不需要标志变量。

更新2:

您可以使用事件onmouseout和onmouseover

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

onmouseout-检测光标何时离开。


为了清楚起见,我已经编辑了问题。该解决方案如何避免设置标志的需要?另外,我在菜单上使用onmousedown()onmouseup(),而不是文本框/输入字段。
1英寸

我不能接受这个答案,因为我不知道它是正确的。您能否回应我在上面的评论中提出的担忧?
1英寸

当然,我可以看到如何将onmouseover和onmouseout与我目前正在执行的操作类似,当鼠标悬停在菜单上时设置标志。但是,我仍然认为您需要在onmouseover中设置一个在onmouseout中清除的标志,以便您单击时知道是否在菜单上方。您能想到不需要设置标志的方法吗?
1 ''

我可以看到你的代码...把它放在一个小提琴......只是把相关的部分和完全确定,如果我不看results..just的代码..
HIRA塔库尔


2

一个更优雅(但性能可能更低)的解决方案:

而不是使用输入的onblur删除菜单,请使用document.onclick,它会在之后触发onblur

但是,这也意味着在单击输入本身时会删除菜单,这是不希望的行为。设置为input.onclickevent.stopPropagation()以避免将单击传播到文档单击事件。


5
好吧,这个答案的问题是,如果不是单击事件最终触发了模糊。如果用户在输入中跳出该怎么办?这将导致模糊事件触发,但弹出菜单仍然可见。
克里斯多夫(Christoph)

0

通过onfocus更改onclick

即使onblur和onclick的相处不是很好,但显然onfocus是可以的。因为即使关闭菜单后,onfocus对于在其中单击的元素仍然有效。

我做到了,它奏效了。


-7

您可以setIntervalonBlur处理程序中使用一个函数,如下所示:

<input id="input" onblur="removeMenu()" ... />

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

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


5
setInterval这是一个坏主意,您永远不要取消它,因此它将持续运行您的功能,并且很可能导致您的网站使用max cpu。setTimeout如果要使用此技术(我不建议这样做),请改用。使您的应用依赖于某些事件的时机是有风险的业务。
Casey

6
危险会鲁宾逊!危险!前面的比赛条件!
GoreDefex

我最初想出了这种解决方案(但setTimeout不是setInterval),但是我不得不250在正确的时间安排之前将其提高到ms。该错误可能仍会存在于速度较慢的设备上,并且使延迟明显。我尝试了onMouseDown,效果很好。我想知道它是否也需要触摸事件。
张天伦

如果您确实想使用onClick,那么间隔这样的东西也不错。但是您希望使用条件而不是间隔的递归超时,这样它不会永远运行。这与Selenium条件等待所使用的模式相同。
该隐会
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.