Currently, I have a HeaderSubmenu component that is designed to show/hide a drop-down menu when a specific button is clicked. However, I am now trying to implement a solution where if the user clicks anywhere else in the application other than on this drop-down menu, it should automatically hide.
For my setup, I am utilizing Vue 2.3.3 along with Vuex and VueRouter.
This is the entry point of my App:
'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)
}
})
Here is the template for the HeaderSubmenu
component:
<template>
<li class="header-submenu">
<!-- Button to toggle the visibility of the drop-down menu -->
<header-link v-bind="$props" :href="to ? false : '#'" @click.native="toggleMenu()">
<slot name="item"></slot>
</header-link>
<!-- Drop-down menu content -->
<ul class="submenu-list" :class="{'open': open, 'animated': animated}" @animationend="displaynone()">
<slot></slot>
</ul>
</li>
</template>
The goal is to call the toggleMenu()
method of this component whenever the user clicks outside the <ul class="submenu-list">
.
I have been considering using a global event bus mechanism where the drop-down menu would be 'registered' to detect any click events within the entire application. If the registered menu is not the element clicked, then its toggleMenu()
method would be triggered. Ideally, other elements with similar behavior could also be registered in this manner.
However, I currently lack a clear understanding of Vue's event system and how to determine if an event was triggered outside of a specific element. Can anyone provide guidance or assistance? Thank you!
====== EDIT ======
After consulting with Bert Evans, I implemented a custom directive as follows:
// directive-clickoutside.js
export default {
bind(el, binding, vnode) {
el.event = function (event) {
// Check if the click occurred outside the element and its children
if (!(el == event.target || el.contains(event.target))) {
// If true, call the method provided in the 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);
Incorporating this directive into my component template:
// HeaderSubmenu component
<template>
<li class="header-submenu">
<!-- Element in the header used to trigger the submenu -->
<header-link v-bind="$props" :href="to ? false : '#'" @click.native="toggle()">
<slot name="item"></slot>
</header-link>
<!-- Submenu content -->
<ul class="submenu-list" :class="{'open': open, 'animated': animated}" @animationend="displaynone()" v-clickout="hide()">
<slot></slot>
</ul>
</li>
</template>
However, upon testing, I encountered the following error when clicking anywhere on the page:
Uncaught TypeError: n.context[e.expression] is not a function
at HTMLBodyElement.t.event (directive-clickoutside.js:7)
What could be causing this issue?