vue-select
vue-select copied to clipboard
prevent click propogation of options
To Reproduce
<!-- method should never be called when clicking an option -->
<div @click="method">
<!-- this should prevent click propogation of options -->
<div @click.stop>
<v-select .../>
</div>
</div>
Steps to reproduce the behavior:
- Have a click listener on an item wrapping v-select
- Wrap v-select with a div and use
@click.stop - Click the dropdown
- Click an option
Expected behavior
Clicking an option should not trigger the parent click event, since it is a child of the div that has @click.stop. However, a click event is triggered. One is not for the dropdown itself, maybe because it's not dynamic? In inspector, the dropdown options are contained within the div, but seem to float outside of it.
@mousedown seems to work for my use case, maybe because @mousedown.prevent is used here?
https://github.com/sagalbot/vue-select/blob/6e9861e787d016239df2f73ddf18bea42704db07/src/components/Select.vue#L67
It would be great if there was a way to also prevent clicks from propogating.
Maybe the options should prevent/stop all three of these?
- @mousedown
- @mouseup
- @click
Also, it looks like clicking on the dropdown menu itself doesn't stop click propogation:
https://github.com/sagalbot/vue-select/blob/6e9861e787d016239df2f73ddf18bea42704db07/src/components/Select.vue#L56
This has posed an interesting problem.
Say you have a modal with a backdrop, and user can click backdrop to close modal. In that modal, you also have a dropdown menu, whose length exceeds that of the modal.
In previous versions of vue-select, this did not seem to be an issue. But now, since the click event of an option is bubbled upwards, the modal's backdrop gets the click, and the modal closes.
@alex-dow : yes, I was just going to post an issue about modal closing unexpectedly...
I have a similar issue, click event and the click-outside solution I am using clash. My modal opens for a brief second, then it disappears when the click event bubbles and kicks in. Has anybody found a solution?
Hmm.. I know this isn't a perfect solution but I also encountered this issue (and thought I was me who was doing something wrong) when I wanted to place a dropdown inside a bootstrap modal.
I added data-backdrop="static" data-keyboard="false" to the button responsible for triggering the modal, which prevents it from disappearing and stays there unless it is specifically closed with a dedicated button. I understand it not the best solution and just a mere workaround but that's a sacrifice I am willing to make for now 🤷 ...
I also ran into this issue while using vue-dismiss, and I tried a lot of things to stop the option click event propagating but had no luck.
My solution was to create a custom version of vue-dismiss that also has an update() hook and lets you pass in an active value (which you can bind to a reactive value that updates on vue-select's @open / @close events). I'm not actually sure why vue-dismiss doesn't have an update() hook, makes the watch value you can pass in kind of useless as it is only used during initial binding of the directive.
Custom vue-dismiss
import Vue from 'vue'
const elementActive = '_vueDismissActive'
const elementClickKey = '_vueDismissClick'
const elementKeyupKey = '_vueDismissKeyup'
Vue.directive('onDismiss', {
bind(el, binding) {
const callback = typeof binding.value === 'function' ? binding.value : binding.value.callback
el[elementActive] = Object.prototype.hasOwnProperty.call(binding.value, 'active')
? Boolean(binding.value.active)
: true
if (typeof document !== 'undefined' && !el[elementClickKey]) {
const handler = function (event) {
if (el[elementActive]) {
if (event.keyCode) {
event.keyCode === 27 && callback()
} else if (!(event.target === el) && !el.contains(event.target)) {
callback()
}
}
}
setTimeout(function() {
document.addEventListener('mousedown', handler)
document.addEventListener('keyup', handler)
}, 10)
el[elementClickKey] = el[elementKeyupKey] = handler
}
},
unbind(el) {
if (el[elementClickKey] && typeof document !== 'undefined') {
document.removeEventListener('mousedown', el[elementClickKey])
document.removeEventListener('keyup', el[elementKeyupKey])
delete el[elementActive]
delete el[elementClickKey]
delete el[elementKeyupKey]
}
},
update(el, binding) {
setTimeout(function() {
if (typeof binding.value !== 'function') {
el[elementActive] = Object.prototype.hasOwnProperty.call(binding.value, 'active')
? Boolean(binding.value.active)
: true
}
}, 250)
}
})
Not an ideal solution, but it works well. A couple of things to note:
- Events don't unbind when
activeistrue, it just bypasses the event handler (unlike with the originalvue-select'swatchnon-reactive value). - A delay is added to the code in the
updatehook, to prevent dismissing on clicks of different lengths. I experimented a bit and250msseems to work well, but you can change it if you are experiencing issues.
Basic Usage Example
<template>
<div v-if="modalIsOpen">
<div v-on-dismiss="{ callback: onDismiss, active: selectIsOpen }">
<v-select @open="selectIsOpen = true" @close="selectIsOpen = false" />
</div>
</div>
</template>
<script>
export default {
data() {
return {
modalIsOpen: true,
selectIsOpen: false
}
},
methods: {
onDismiss() {
this.modalIsOpen = false
}
}
}
</script>
any solution to this?
how to solve this, i'm using modal and will trigger close if the dropdown is on the top of backdrop area
This worked for me:
<template>
<VueSelect
,,,
ref="vueSelect"
@option:selecting="handleSelect"
>
...
</VueSelect>
...
...
methods: {
handleSelect(selectedOption) {
const { closeOnSelect, searchEl } = this.$refs.vueSelect;
if (!closeOnSelect && searchEl) {
searchEl.blur();
}
...
},
...
I dont know if solve the issue, in a similar situation adding @click.stop="" preserve the select function without propagating the click event to outer components