admin管理员组文章数量:1313140
I've a HeaderSubmenu ponent which can show/hide a drop-down menu if a button is clicked. Okay, but now I try to find a good solution to make that if the user clicks anywhere in the app but not on this drop-down menu, it hides.
I'm using Vue 2.3.3 with Vuex and VueRouter.
my App entry point :
'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)
}
})
template of HeaderSubmenu
ponent :
<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>
This ponent is somewhere in my app, and I want to call his toggleMenu()
method when the users clicks elsewhere than on the <ul class="submenu-list">
.
I thought of a global event bus where my drop-down menu should be "registred" and detects on a global @click event on my App. If the registered menu is not the element clicked we'd call his toggleMenu()
method if not.
(Ideally I could register other elements which have the same behaviour.)
... But I don't master vue event system at all. How can check if an event is not on some element ? I've no idea of how to achieve this. Can you help me ? Thank you !
====== EDIT ======
With the help of Bert Evans I used this custom directive :
// 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);
I used this in my ponent template :
// HeaderSubmenu ponent
<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>
But this code fails when I click anywhere on the page :
Uncaught TypeError: n.context[e.expression] is not a function
at HTMLBodyElement.t.event (directive-clickoutside.js:7)
Where is the problem ?
I've a HeaderSubmenu ponent which can show/hide a drop-down menu if a button is clicked. Okay, but now I try to find a good solution to make that if the user clicks anywhere in the app but not on this drop-down menu, it hides.
I'm using Vue 2.3.3 with Vuex and VueRouter.
my App entry point :
'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)
}
})
template of HeaderSubmenu
ponent :
<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>
This ponent is somewhere in my app, and I want to call his toggleMenu()
method when the users clicks elsewhere than on the <ul class="submenu-list">
.
I thought of a global event bus where my drop-down menu should be "registred" and detects on a global @click event on my App. If the registered menu is not the element clicked we'd call his toggleMenu()
method if not.
(Ideally I could register other elements which have the same behaviour.)
... But I don't master vue event system at all. How can check if an event is not on some element ? I've no idea of how to achieve this. Can you help me ? Thank you !
====== EDIT ======
With the help of Bert Evans I used this custom directive :
// 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);
I used this in my ponent template :
// HeaderSubmenu ponent
<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>
But this code fails when I click anywhere on the page :
Uncaught TypeError: n.context[e.expression] is not a function
at HTMLBodyElement.t.event (directive-clickoutside.js:7)
Where is the problem ?
Share Improve this question edited Jun 17, 2017 at 20:22 Thaledric asked Jun 17, 2017 at 12:53 ThaledricThaledric 5592 gold badges8 silver badges17 bronze badges 4- 1 Possible duplicate of Detect click outside element – Bert Commented Jun 17, 2017 at 13:49
- Thanks, I tried to use a custom directive, but it ends with an infinite update loop. I edited the post to show you that. – Thaledric Commented Jun 17, 2017 at 19:58
-
What does
toggleMenu
do? – Bert Commented Jun 17, 2017 at 20:13 -
toogleMenu invert the state of appearance of the submenu (show/hide). It certainly was the origin of infinite loop. I changed to use
toogle()
when click on the link, and justehide()
when click away from the submenu. But a new problem occurs in the custom directive (post edited). – Thaledric Commented Jun 17, 2017 at 20:24
2 Answers
Reset to default 10The issue is here.
v-clickout="hide()"
Effectively what you are doing is setting v-clickout
to the result of hide()
. Instead, just pass it the hide
method.
v-clickout="hide"
In general with Vue, in your template, if you are just wanting the template to call a function and not do anything special, just pass it the name of the function.
Here's how to create the click-outside directive that works with Vue 3:
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)
},
})
本文标签: javascriptTrigger components methods when click elsewhereStack Overflow
版权声明:本文标题:javascript - Trigger components methods when click elsewhere - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741947585a2406513.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论