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 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?
Share Improve this question edited Nov 29, 2020 at 13:38 Brian Tompsett - 汤莱恩 5,89372 gold badges61 silver badges133 bronze badges asked Sep 3, 2018 at 18:47 Peter MatiskoPeter Matisko 2,2531 gold badge27 silver badges50 bronze badges1 Answer
Reset to default 5I'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./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 the blue box. The blue box receives the click event first, then it bubbles up to the yellow then red boxes, as expected.
- Click the yellow box. The yellow then red boxes receive the click event, then once it bubbles to the document element (of which the
click-outside
directive has attached an event listener to) theclick-outside
handler is called. CallingstopPropagation()
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./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?