I am struggling with events propagation. The task is to prevent clicking on anything alse, if the data is not saved. So, The left div contains a tree of options. After clicking on an item, a setting in the right div is showed. I want to warn the user, when he tries to click outside the settings div, that the data is not saved.
The code looks like this:
<div class="row">
<div class="col-sm-6">
<tree
:data="treeData">
.. some tree data
</tree>
</div>
<div class="col-sm-6">
<div class="treeOptionEdit" v-if="showEditBox" v-click-outside.stop="checkIfSaved">
... setting input fields
vue-outside-events
package is used to detect clicks outside div. It works fine. The problem is, that this code, doesn't do anything:
checkIfSaved : function (event, el)
{
event.stopPropagation();
event.preventDefault();
},
The click gets propagated to the parent anyway. If I put an event @click
to the parent, it is fired an all clicks.
Does stop propagation work only from parent to children?
I'm not familiar with the v-click-outside
implementation, but "click outside" events are typically implemented by using event delegation on the document/body element, and so by that time the event has already propagated and cannot be stopped.
Here's a small example of what's going on:
Vue.directive('click-outside', {
bind(el, { value }) {
el._handler = e => {
if (!el.contains(e.target)) {
value(e);
}
};
document.addEventListener('click', el._handler);
},
unbind(el) {
document.removeEventListener('click', el._handler);
},
});
new Vue({
el: '#app',
methods: {
onClick(e, color) {
alert(`Clicked the ${color} box.`);
},
onClickOutside(e, color) {
e.stopPropagation();
alert(`Clicked outside of ${color} box.`);
},
},
});
.box { padding: 20px; }
.red { background-color: red; }
.yellow { background-color: yellow; }
.blue { background-color: blue; }
<script src="https://rawgit.com/vuejs/vue/dev/dist/vue.js"></script>
<div id="app">
<div class="box red" @click="onClick($event, 'red')">
<div class="box yellow" @click="onClick($event, 'yellow')">
<div class="box blue" @click="onClick($event, 'blue')" v-click-outside="e => onClickOutside(e, 'blue')">
</div>
</div>
</div>
</div>
click-outside
directive has attached an event listener to) the click-outside
handler is called. Calling stopPropagation()
at this moment does nothing because the event has already bubbled to the top.I don't believe the .stop
modifier does anything in your example because v-click-outside
doesn't support this modifier (.stop
is only supported by v-on
; v-click-outside
would need to support it).
So to solve your problem you need to change the order of events: v-click-outside
needs to be handled first before @click
.
You can achieve this by changing the v-click-outside
implementation by using event capturing as follows:
Vue.directive('click-outside', {
bind(el, { value }) {
el._handler = e => {
if (!el.contains(e.target)) {
value(e);
}
};
// *** Using capturing ***
document.addEventListener('click', el._handler, { capture: true });
},
unbind(el) {
document.removeEventListener('click', el._handler, { capture: true });
},
});
new Vue({
el: '#app',
methods: {
onClick(e, color) {
alert(`Clicked the ${color} box.`);
},
onClickOutside(e, color) {
e.stopPropagation();
alert(`Clicked outside of ${color} box.`);
},
},
});
.box { padding: 20px; }
.red { background-color: red; }
.yellow { background-color: yellow; }
.blue { background-color: blue; }
<script src="https://rawgit.com/vuejs/vue/dev/dist/vue.js"></script>
<div id="app">
<div class="box red" @click="onClick($event, 'red')">
<div class="box yellow" @click="onClick($event, 'yellow')">
<div class="box blue" @click="onClick($event, 'blue')" v-click-outside="e => onClickOutside(e, 'blue')">
</div>
</div>
</div>
</div>
Why not submit a pull request to vue-outside-events to support this feature?