Для вашего случая, как уже отмечалось в комментариях к вопросу, вы должны обрабатывать множество ситуаций, например, @focus
и @click
будут вызваны подряд, @blur
из <button>
и @blur
из <ul></li>
будет срабатывать при нажатии на любой из них.
Это не очень хорошая идея .Но вы можете проверить Эта скрипка , это одно решение с setTimeout
& clearTimeout
.Тогда вы, возможно, уже видели, что он задерживается на 100 мсек setTimeout(()=>{}, 100)
(я добавил несколько журналов, вы можете открыть консоль браузера, чтобы проверить рабочий процесс). Причина в том, что нам нужно подождать достаточно времени , чтобы убедиться, что следующий обработчик событий (например, сначала сработал фокус, затем будет активирован щелчок) может очистить предыдущий setTimeout
во время , если только меню не может быть открыто сначала, а затем снова закрыто.(PS: для некоторых старых машин 100 мс может быть недостаточно, это зависит от того, как быстро закончится текущий рендеринг)
Одно решение :
удалить @focus
и @blur
, когда this.showMenu
истинно (открыто), добавить один слушатель = click
для Dom = документа, он будет выполняться this.hide()
при запуске.
Затем внутри this.hide()
удалите этот прослушиватель = click
из Dom = документа.
, чтобы предотвратить свертывание меню принажмите на кнопку и в меню, добавьте модификатор = stop, он остановит распространение события щелчка на узлы Dom верхнего уровня.
Если вы оберните <button>
и <ul>
водин <div>
, тогда нужно только добавить модификатор = stop
вроде <template><div @click.stop><button></button<ul>...<ul></div></template>
.
Ниже одно демо :
Vue.config.productionTip = false
new Vue({
el: "#app",
data: {
showMenu: false,
items: ['Option 1', 'Option 2', 'Option 3', 'Option 4']
},
computed: {
listClass: function() {
if (this.showMenu) {
return 'show';
}
return '';
}
},
methods: {
toggle: function() {
this.showMenu = !this.showMenu
this.showMenu && this.$nextTick(() => {
document.addEventListener('click', this.hide)
})
},
hide: function() {
this.showMenu = false
document.removeEventListener('click', this.hide)
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
ul {
list-style: none;
display: none;
}
li {
padding: 5px;
border: 1px solid #000;
}
li:hover {
cursor: pointer;
background: #aaa;
}
.show {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<h3>
This is one demo:
</h3>
<button @click.stop="toggle" tabindex="0">
Toogle menu
</button>
<ul :class="listClass" @click.stop>
<li v-for="(item, key) in items" tabindex="0">{{ item }}</li>
</ul>
</div>