vue
vue copied to clipboard
Add non-passive event modifier
What problem does this feature solve?
An event modifier to support passive
events was added in #5132.
In the last couple of years, browsers have adopted the passive
behavior by default for touchstart
and touchmove
events (reference). Thus, to be able to cancel one of these events by calling e.preventDefault()
, you need to explicitly pass { passive: false }
when adding the event listener.
With the current API this is impossible to achieve in a Vue template (as far as I can tell). You must manually add and remove the event listener in a component hook like so:
this.$refs.someElement.addEventListener('touchstart', this.start, { passive: false });
this.$refs.someElement.addEventListener('touchmove', this.move, { passive: false });
// later
this.$refs.someElement.removeEventListener('touchstart', this.start);
this.$refs.someElement.removeEventListener('touchmove', this.move);
What does the proposed API look like?
An event modifier that does the opposite of the passive
event modifier, specifying the option as false
instead of true
.
Unsure of the naming - perhaps nonPassive
, active
, assertive
, intentional
.
<div
@touchstart.active="start"
@touchmove.active="move"
></div>
In vue/src/core/vdom/helpers/update-listeners.js:22
, normalizeEvent
always returns an object with a set passive
property.
I’m not familiar with Vue’s source code, but does that mean that when the .passive
modifier is not present, each event handler is always declared as active? This seems wrong in the light of browser’s changing the default for certain event types to be passive by default (see Improving scrolling performance with passive listeners). If .passive
is not present, I don’t expect event handlers to be marked as not passive.
With regards to the feature request itself, I think “active” would be a natural choice here.
nonPassive seems like the more logical opposite of passive. Just wondering what wins if you do
@scroll.passive.nonPassive="..."
vs
@scroll.nonPassive.passive="..."
Any difference at all? which one wins? If neither is passed, I believe passive should be left as undefined.
Any1 figure out a workaround to this? touchstart & conditionally setting preventDefault in the running code doesn't seem to work to cancel the event.
In Vue 3, the following code generates a "passive : false" event listener attached to that div
.
<div
@touchstart="function"
></div>
I think vue need this feature, otherwise modern browser will report tons of warnings, now edge/chrome need set the passive to true or false explictly in the addEventListener for some events, for example, touchstart, wheel and so on, otherwise it will report warnings, it is already disaster for me, I don't have easy way to solve it.

Here is my code clip:
...
<div v-if="img.focused == true && img.selected == false" class="photo_selection"
v-on:click="on_toggle_photo_checkbox(img, true, $event, imgTypePos + start_list_idx_in_vw)"
v-on:touchstart.prevent="on_toggle_photo_checkbox(img, true, $event, imgTypePos + start_list_idx_in_vw)">
...
If I didn't use the v-on:touchstart/wheel, and handle these events with raw javascript DOM API addEventListener, there is so much effort for me, because the elements binding "touchstart" event in my project are dynamically created or destroyed by v-if.
Can vue team consider adding a new modifier to pass event option with passive: false?
@wangf1978 short term solution, you could add a ref and use vueuse's useEventListener(divRef, 'touchstart', eventCallback, { passive: true })
But I am unsure if that solution would work for you as I am unsure where some of your variables are coming from.
@wangf1978 short term solution, you could add a ref and use vueuse's useEventListener(divRef, 'touchstart', eventCallback, { passive: true })
But I am unsure if that solution would work for you as I am unsure where some of your variables are coming from.
@WORMSS Thanks for your wonderful solution, but it seems not to be so fit for my scenario:
The div element is generated and destroyed dynamically and frequently by v-if
directive in virtual DOM, actually I don't have chance to insert my javascript code for them, and all the related events are controlled v-on:
directive, so event modifier seems to be the more direct, elegant and natural way.
the divRef
would be what links the elements, the events would be added/removed as soon as the elements are added/removed from the dom. (it will internally add a watcher on divRef
and when it changes, does what it needs to to add the event.
<template>
<div
ref="divRef"
v-if="condition"
@click="yourevent"
/>
<script setup>
import { unref } from 'vue';
import { useEventListener } from '@vueuse/core';
const divRef = ref(null);
const eventCallback = (evt) => {
on_toggle_photo_checkbox(unref(img), true, evt, unref(imgTypePos) + unref(start_list_idx_in_vw));
};
useEventListener(divRef, 'touchstart', eventCallback, { passive: true });
</script>
again, I've had to guesstimate some of this, so just assumed most of it are refs/computed, but unref
doesn't care if it's a value/ref/computed, it will get the value regardless.
In Vue 3, the following code generates a "passive : false" event listener attached to that
div
.<div @touchstart="function" ></div>
Even if it's true, then it doesn't do that in this case: <div v-on="{ wheel: wheelHandler }" ...
- it still produces the "Added non-passive event listener to a scroll-blocking 'wheel' event." warning.