Javascript 自定义Vue下拉列表保持焦点
我试图在这里找到解决方案,但没有找到,于是决定写一个问题 因此,我尝试在Vue中构建一个简单的下拉菜单组件,该组件具有切换按钮,用户可以自由单击并在项目周围设置选项卡,但一旦焦点离开该组件,菜单就会崩溃 现在,我使用Javascript 自定义Vue下拉列表保持焦点,javascript,vue.js,vuejs2,frontend,Javascript,Vue.js,Vuejs2,Frontend,我试图在这里找到解决方案,但没有找到,于是决定写一个问题 因此,我尝试在Vue中构建一个简单的下拉菜单组件,该组件具有切换按钮,用户可以自由单击并在项目周围设置选项卡,但一旦焦点离开该组件,菜单就会崩溃 现在,我使用@blur和@focus事件保持焦点,但是切换按钮有问题。如果我将相同的侦听器附加到它,那么单击它会显示并立即隐藏菜单,因此您必须再次单击以展开它 下面是演示问题的示例 但是,如果我删除了侦听器,那么在焦点位于组件内部之后单击按钮是有问题的。 我猜我的方法是错误的,所以这里是预期的行
@blur
和@focus
事件保持焦点,但是切换按钮有问题。如果我将相同的侦听器附加到它,那么单击它会显示并立即隐藏菜单,因此您必须再次单击以展开它
下面是演示问题的示例
但是,如果我删除了侦听器,那么在焦点位于组件内部之后单击按钮是有问题的。
我猜我的方法是错误的,所以这里是预期的行为:
切换按钮就是一个切换按钮。单击时应折叠/展开列表
- 只有在列表关闭时单击
切换
按钮,才能展开列表
- 当列表展开时,单击
切换
按钮或将焦点从列表中调出,可以折叠列表。这包括将焦点放在按钮上(即,单击切换按钮展开列表,然后单击外部某处而不聚焦任何项目)
编辑:多亏了@Sphinx,我成功地让下拉菜单按我的预期工作。这是更新的。我不确定这是否修复了您试图完成的任务,但您可以尝试使用
@mouseenter和@mouseout
<button
@click="toggle"
tabindex="0"
@mouseenter="itemFocus"
@mouseout="itemBlur"
>
Toogle menu
图格菜单
问题是:我大约有40%的把握我理解这个问题
根据你的描述,这个按钮似乎不应该是一个开关,而是一个打开的按钮
模板:
<div id="app">
<button
@click="showMenu"
tabindex="0"
@focus="itemFocus"
@blur="itemBlur"
>
Toogle menu
</button>
<ul :class="listClass">
<li
v-for="(item, key) in items"
tabindex="0"
@focus="itemFocus"
@blur="itemBlur"
>{{ item }}</li>
</ul>
</div>
这将在菜单关闭时打开菜单,但不会关闭它
这些具有多种交互方式的UI类型的诀窍是设置UI元素的状态,并让UI元素反映该状态
所以我做的是:
- 如果鼠标进入,菜单应打开
- 如果鼠标离开,菜单应关闭
- 如果是聚焦的,菜单应该是打开的
- 如果模糊,菜单应该关闭
因此,我的解决方案如下:
HTML:
<div id="app">
<button
tabindex="0"
@focus="isMenuOpen = true"
@blur="isMenuOpen = false"
@mouseenter="isMenuOpen = true"
@mouseout="isMenuOpen = false"
>
Toogle menu
</button>
<ul :class="listClass">
<li
v-for="(item, key) in items"
tabindex="0"
@focus="isMenuOpen = true"
@blur="isMenuOpen = false"
@mouseenter="isMenuOpen = true"
@mouseout="isMenuOpen = false"
>{{ item }}</li>
</ul>
</div>
对于您的情况,正如问题下的评论已经指出的那样,您必须处理许多情况,例如@focus
和@click
将连续触发,
的@blur
和
的@blur
将在单击其中任何一个时触发
这不是一个好主意。但是您可以检查,它是一个解决方案,具有setTimeout
和cleartimout
。然后您可能已经看到它延迟了100ms,方法是setTimeout(()=>{},100)
(我添加了一些日志,您可以打开浏览器控制台来检查工作流程)原因是我们必须等待足够的时间,以确保下一个事件处理程序(如先触发焦点,然后再触发单击)能够及时清除上一个设置超时
,除非菜单可能先打开,然后再次关闭。(PS:对于一些旧机器,100毫秒可能不够,这取决于当前渲染完成的速度)
一种解决方案:
移除@focus
和@blur
当this.showMenu
为true(已打开)时,添加一个侦听器=单击它将在触发时执行的Dom=文档this.hide()
然后在this.hide()
中,从Dom=document中删除该侦听器=单击
为了防止在单击按钮和菜单时合并菜单,请添加修饰符=stop,它将停止单击事件向上层Dom节点的传播
如果将
和
包装为一个
,则只需添加修饰符=停止
如{
document.addEventListener('click',this.hide)
})
},
隐藏:函数(){
this.showMenu=false
document.removeEventListener('单击',this.hide)
}
}
})
正文{
背景:#20262E;
填充:20px;
字体系列:Helvetica;
}
#应用程序{
背景:#fff;
边界半径:4px;
填充:20px;
过渡:均为0.2s;
}
保险商实验室{
列表样式:无;
显示:无;
}
李{
填充物:5px;
边框:1px实心#000;
}
李:悬停{
光标:指针;
背景:#aaa;
}
.表演{
显示:块;
}
这是一个演示:
图格菜单
{{item}
按钮上同时有一个@焦点
和@单击
,如果您单击它(如果您以前没有单击过它),这两个按钮都会被触发。您可能只想从按钮中删除@focus
。@Tommos的问题是正确的,但请注意它依赖于浏览器。在某些浏览器上,当您单击按钮时,浏览器会在单击
事件之前触发焦点
事件。这就是你所看到的行为的原因。focus
事件打开菜单,然后单击事件将其切换,从而将其关闭。当您第二次单击按钮时,不会出现focus
事件,因为该按钮在上一次单击时已具有焦点。@Tommos谢谢,我知道为什么会发生这种情况。但是,如果删除@focus
处理程序,则在单击任何列表项后,单击切换
按钮无法折叠列表。几乎。但我希望能够在列表打开时单击切换按钮来折叠列表。谢谢你的帮助,非常感谢。我记得用d的方法
<div id="app">
<button
tabindex="0"
@focus="isMenuOpen = true"
@blur="isMenuOpen = false"
@mouseenter="isMenuOpen = true"
@mouseout="isMenuOpen = false"
>
Toogle menu
</button>
<ul :class="listClass">
<li
v-for="(item, key) in items"
tabindex="0"
@focus="isMenuOpen = true"
@blur="isMenuOpen = false"
@mouseenter="isMenuOpen = true"
@mouseout="isMenuOpen = false"
>{{ item }}</li>
</ul>
</div>
// simplified for clarity
new Vue({
el: "#app",
data: {
showMenu: false,
items: [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ],
isMenuOpen: false,
},
computed: {
listClass() { // you can omit the `: function` in new ES standard
return this.isMenuOpen ? 'show' : ''; //ternary op saves lines
},
methods: {
// not needed, as it's done in HTML
// toggle: function(){
// this.showMenu = !this.showMenu
//},
/*
we no longer need these methods:
itemFocus: function() {
var self = this;
Vue.nextTick(function() {
if(!self.showMenu) {
self.showMenu = true;
}
});
},
itemBlur: function() {
var self = this;
Vue.nextTick(function() {
if(self.showMenu) {
self.showMenu = false;
}
});
}
*/
}
})