vue icon indicating copy to clipboard operation
vue copied to clipboard

Add non-passive event modifier

Open tobyzerner opened this issue 5 years ago • 9 comments

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>

tobyzerner avatar Oct 30 '19 00:10 tobyzerner

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.

kleinfreund avatar Nov 10 '19 18:11 kleinfreund

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.

WORMSS avatar Nov 20 '19 14:11 WORMSS

Any1 figure out a workaround to this? touchstart & conditionally setting preventDefault in the running code doesn't seem to work to cancel the event.

inspire22 avatar Dec 14 '20 00:12 inspire22

In Vue 3, the following code generates a "passive : false" event listener attached to that div.

<div
  @touchstart="function"
></div>

Kvothe1997 avatar Dec 17 '20 01:12 Kvothe1997

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.

image

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 avatar Nov 24 '22 09:11 wangf1978

@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 avatar Nov 24 '22 10:11 WORMSS

@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.

wangf1978 avatar Nov 24 '22 14:11 wangf1978

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.

WORMSS avatar Nov 24 '22 16:11 WORMSS

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.

VladiStep avatar Mar 19 '24 13:03 VladiStep