How to close a modal by clicking outside:
There are two ways to do, in your case, as you do not have a div around your modal (a staff puts to darken the screen), usually by it you could make this click outside, or as yours does not have, you can use your own body
Because there is a side effect on events, where they propagate both from parent to child elements, and vice versa, this effect is called Bubbling and capturing
You can take advantage of this if you put the event on <body>
as everything is his son, he will capture any click he has given on the screen
But this can also be a problem, since clicking in the middle of the modal will also close it, and should not be what you want
And that’s where the @click.stop
, the additive stop
cancels this side effect on the div where it is present, so you can put it in the modal so that it stops clicking on it causes it to be closed.
Soon in your code would add the following:
Add the event when creating the component, passing the function of toggle
Remembering that it is necessary to remove the event while destroying the component so that it does not attempt to run even after the modal is removed
export default {
data(){...},
methods(){...},
created(){
window.body.addEventListener("click", this.toggle)
},
destroy(){
window.body.removeEventListener("click", this.toggle)
}
}
And change the HTML to add the @click.stop
on the menu/modal tag to prevent the body event from running inside the modal
<template>
<div class="cont-icom-seting">
<i @click.stop="toggle" class="fas fa-cog"></i>
<div v-show="isOpen">
<!-- Adcionado aqui ⬇️ -->
<div class="menuSusoenco" @click.stop>
<div class="contItensMenuSuspenco">
<div class="menuSusoenco-item">conteudo</div>
<div class="menuSusoenco-item">conteudo</div>
<div class="menuSusoenco-item">conteudo</div>
<hr />
<div class="menuSusoenco-item">Logout</div>
</div>
</div>
</div>
</div>
</template>
However, a recommendation
Using the body, which is not directly accessible by Vue components, induces that you need to use an event that is not from Vue, and even that you should remove it after the component is destroyed, and if you forget this can cause problems.
So I would recommend putting on what people call wrapper, that is nothing more than a div that stands around everything.
So with a wrapper, you can make a div occupy the entire screen and have the same function that you would be putting on the body, this would allow you to use everything that is within the scope of the component and it be totally independent, avoiding generating any kind of problem.
Soon it would be the following:
Your JS would be as it is without the need to change anything
the HTML, would have to add the wrapper:
<template>
<!-- wrapper com toggle aqui ⬇️ -->
<div class="modal-wrapper" @click="toggle">
<div class="cont-icom-seting">
<i @click.stop="toggle" class="fas fa-cog"></i>
<div v-show="isOpen">
<!-- Adcionado stop aqui ⬇️ -->
<div class="menuSusoenco" @click.stop>
<div class="contItensMenuSuspenco">
<div class="menuSusoenco-item">conteudo</div>
<div class="menuSusoenco-item">conteudo</div>
<div class="menuSusoenco-item">conteudo</div>
<hr />
<div class="menuSusoenco-item">Logout</div>
</div>
</div>
</div>
</div>
</div>
</template>
But then it would be necessary to make the wrapper occupy the entire screen, so you’d need to tweak the CSS, adding:
.modal-wrapper {
position: fixed;
width: 100vw;
height: 100vh;
}
And an observation
You may even be using Vue 3, but this syntax is not new, and is also valid for Vue 2
Alternatives
There are other ways of doing modals as well, another alternative that may be of interest to you is this form:
Building a Modal Component with Vue.js