单击其他地方时触发组件方法
Posted
技术标签:
【中文标题】单击其他地方时触发组件方法【英文标题】:Trigger components methods when click elsewhere 【发布时间】:2017-11-20 03:40:40 【问题描述】:我有一个 HeaderSubmenu 组件,如果单击按钮,它可以显示/隐藏下拉菜单。好的,但是现在我尝试找到一个好的解决方案,如果用户单击应用程序中的任何位置而不是此下拉菜单,它就会隐藏。
我将 Vue 2.3.3 与 Vuex 和 VueRouter 一起使用。
我的应用入口点:
'use strict';
import Vue from 'vue';
import VueRouter from 'vue-router';
import Vuex from 'vuex';
Vue.use(VueRouter);
Vue.use(Vuex);
import store_data from 'store';
import router from 'routes';
import App from 'app.vue';
var store = new Vuex.Store(store_data);
new Vue(
el: '#app',
store,
router: router,
render: function (createElement)
return createElement(App)
)
HeaderSubmenu
组件的模板:
<template>
<li class="header-submenu">
<!-- button to show/hide the drop-down menu -->
<header-link v-bind="$props" :href="to ? false : '#'" @click.native="toggleMenu()">
<slot name="item"></slot>
</header-link>
<!-- drop-down menu -->
<ul class="submenu-list" :class="'open': open, 'animated': animated" @animationend="displaynone()">
<slot></slot>
</ul>
</li>
</template>
这个组件在我的应用程序的某个地方,当用户点击<ul class="submenu-list">
以外的其他地方时,我想调用他的toggleMenu()
方法。
我想到了一个全局事件总线,我的下拉菜单应该“注册”并在我的应用程序上检测全局 @click 事件。如果注册的菜单不是被点击的元素,我们会调用他的toggleMenu()
方法。
(理想情况下,我可以注册具有相同行为的其他元素。)
...但是我根本不掌握vue事件系统。 如何检查事件是否不在某个元素上? 我不知道如何实现这一目标。你能帮助我吗 ?谢谢!
====== 编辑 ======
在 Bert Evans 的帮助下,我使用了这个自定义指令:
// directive-clickoutside.js
export default
bind(el, binding, vnode)
el.event = function (event)
// here I check that click was outside the el and his childrens
if (!(el == event.target || el.contains(event.target)))
// and if it did, call method provided in attribute value
vnode.context[binding.expression](event);
;
document.body.addEventListener('click', el.event)
,
unbind(el)
document.body.removeEventListener('click', el.event)
,
;
// main.js
import clickout from 'utils/directive-clickoutside';
Vue.directive('clickout', clickout);
我在我的组件模板中使用了这个:
// HeaderSubmenu component
<template>
<li class="header-submenu">
<!-- élément du header servant à ouvrir le sous-menu -->
<header-link v-bind="$props" :href="to ? false : '#'" @click.native="toggle()">
<slot name="item"></slot>
</header-link>
<!-- sous-menu -->
<ul class="submenu-list" :class="'open': open, 'animated': animated" @animationend="displaynone()" v-clickout="hide()">
<slot></slot>
</ul>
</li>
</template>
但是当我点击页面上的任何地方时,这段代码会失败:
Uncaught TypeError: n.context[e.expression] is not a function
at htmlBodyElement.t.event (directive-clickoutside.js:7)
问题出在哪里?
【问题讨论】:
Detect click outside element的可能重复 谢谢,我尝试使用自定义指令,但它以无限更新循环结束。我编辑了帖子以向您展示。toggleMenu
是做什么的?
toogleMenu 反转子菜单的显示状态(显示/隐藏)。这当然是无限循环的起源。当点击链接时我改为使用toogle()
,当点击远离子菜单时,我改为使用hide()
。但是自定义指令中出现了一个新问题(已编辑)。
【参考方案1】:
问题就在这里。
v-clickout="hide()"
实际上,您正在做的是将v-clickout
设置为hide()
的结果。相反,只需将 hide
方法传递给它。
v-clickout="hide"
一般来说,在你的模板中,如果你只是想让模板调用一个函数而不做任何特殊的事情,只需将函数的名称传递给它。
【讨论】:
哦,是的,所以它不像事件指令!我在点击事件上添加了stop
修饰符。谢谢 !我有最后一个问题:如果我有两个子菜单,当我打开第一个子菜单并打开另一个子菜单时,第一个子菜单不会关闭(当然是因为切换按钮的停止修饰符)。我怎样才能做到这一点?
@Thaledric 首先,事件指令是相同的。您可以将它们设置为等于不带括号的名称。我很难想象第二种情况的情况。这些是真正的子菜单(菜单的子菜单)吗?也许是一个新问题,所以您可以提供所有详细信息。
谢谢!我在这里发布了一个新问题:***.com/questions/44613980/…【参考方案2】:
以下是创建适用于 Vue 3 的 click-outside 指令的方法:
app.directive('click-outside',
// bind
beforeMount(el, binding)
el.clickOutsideEvent = function (event)
if (!(el == event.target || el.contains(event.target)))
binding.value(event)
document.body.addEventListener('click', el.clickOutsideEvent)
,
// unbind
unmounted(el)
document.body.removeEventListener('click', el.clickOutsideEvent)
,
)
【讨论】:
以上是关于单击其他地方时触发组件方法的主要内容,如果未能解决你的问题,请参考以下文章
为啥单击功能会在 Angular 2 中为自定义组件触发两次