I have an overlay that when you click on it makes it disappear! I have an absolute positioned card on top of this overlay that when clicked on should not make the overlay disappear but it does. How can I make it so clicking on anywhere on the screen but the card makes the overlay disappear?
<div v-if="displayOverlay" @click="!displayOverlay">
<div > Hello </div>
</div>
.card{
position: absolute;
bottom: 400px;
}
CodePudding user response:
click
event bubbles.
Practically, if a click is triggered on <Child />
in this structure:
<GrandParent>
<Parent>
<Child />
</Parent>
</GrandParent>
, the event will fire on each element, in this order 1:
- on Child
- on Parent
- on GrandParent
- ... on each of the consequent parents in DOM tree, all the way up to
window
object.
To prevent an event from bubbling you have to call its stopPropagation method. You can do this at any level, because the events described above are fired in sequence, not in parallel.
To prevent propagation to parents and also prevent any subsequent event handlers registered on the current element from being fired, call stopImmediatePropagation method on the event.
Vue provides event modifiers, significantly reducing the boilerplate typically associated with calling event native methods.
In your case, you have to use the .stop
modifier on .card
to prevent it from bubbling to .overlay
:
<div v-if="displayOverlay" @click="displayOverlay = false">
<div @click.stop> Hello </div>
</div>
Demo 2, 3:
new Vue({
el: '#app',
data: () => ({
displayCard: true
})
})
.overlay {
position: absolute;
background-color: rgba(0,0,0,.21);
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.card {
height: 200px;
width: 300px;
padding: 1rem;
background-color: white;
}
body {
margin: 0;
}
#app {
min-height: 100vh;
}
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<div id="app" @click="displayCard = true">
<div v-if="displayCard" @click.stop="displayCard = false">
<div @click.stop>Test</div>
</div>
<button v-if="!displayCard">Reopen overlay</button>
</div>
NOTES:
1 - the order above is the native firing order. Vue provides a modifier (.capture
) which allows you to change it. Before starting to run the native sequence, Vue checks all parents (starting from window
all the way down to the clicked element) and if any of them has the .capture
modifier, their event handler will be run at that time (before running the handlers on any of its children). This doesn't mean the event handler will run twice, though. If it has been run in capture phase, it won't be run in bubbling phase. .capture
won't prevent child handlers from running. They will still be run, but after the parent's handler.
2 - I've fixed the @click
event handler on the overlay div:
!displayOverlay
doesn't execute anything it just returns the negative boolean value of currentdisplayOverlay
value;- to assign
false
todisplayOverlay
you'd need:@click="() => { displayOverlay = false }"
. Vue allows us to write a shorter version:@click="displayOverlay = false"
(will auto wrap it with an anonymous function).
3 - <div @click.stop>
is short for <div @click="$event => $event.stopPropagation()">