click
event bubbles.
When a click event is triggered on <Child />
within this hierarchy:
<GrandParent>
<Parent>
<Child />
</Parent>
</GrandParent>
, the event will propagate through each element in the following order 1:
- on Child
- on Parent
- on GrandParent
- ... and so on, up the DOM tree to the
window
.
To stop an event from bubbling further, you can use the stopPropagation method. This can be done at any level since events are fired sequentially, not simultaneously.
If you want to prevent the propagation to parents and block any subsequent event handlers on the current element, you need to call stopImmediatePropagation method on the event.
Vue offers event modifiers which simplify the process of calling native event methods.
In your scenario, you can use the .stop
modifier on .card
to prevent the event from reaching .overlay
:
<div v-if="displayOverlay" class="overlay" @click="displayOverlay = false">
<div class="card" @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/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b0c6c5d5f0829e869e8184">[email protected]</a>/dist/vue.min.js"></script>
<div id="app" @click="displayCard = true">
<div class="overlay" v-if="displayCard" @click.stop="displayCard = false">
<div class="card" @click.stop>Test</div>
</div>
<button v-if="!displayCard">Reopen overlay</button>
</div>
NOTES:
1 - The ordering mentioned above represents the default firing sequence. Vue provides a modifier (.capture
) that allows you to modify it. Before executing the native sequence, Vue checks all parents (from window
down to the clicked element) and if any of them has the .capture
modifier, their event handler will run first (before the children's handlers). However, this does not mean the event handler will run twice. If it runs in capture phase, it won't run in bubbling phase. .capture
doesn't prevent child handlers from running; they will still execute after the parent's handler.
2 - I've corrected the @click
event handler on the overlay div:
3 - <div @click.stop>
is shorthand for
<div @click="$event => $event.stopPropagation()">