Vue: Mouse[enter/over/leave/out] events suppress click event on iOS in Vue 2.6.x

Created on 10 Apr 2019  路  4Comments  路  Source: vuejs/vue

Version

2.6.10

Reproduction link

https://github.com/nathantreid/vue-bug-ios-mouseevent

Steps to reproduce

  1. Using iOS Safari, tap the first or second button. The mouseenter or mouseover event is logged, but the click event is not fired.
  2. Tap the same button again and the click event will fire.
  3. Tap any other button. The mouseleave or mouseout event is logged, but the click event is not fired.
  4. Tap the same button again and the click event will fire.

How to create a reproduction from scratch:
Scenario A: mouseenter / mouseover

  1. Add a mouseenter or mouseover event to an element
  2. Add a button with a click handler inside the element from step 1
  3. Tap on the button.

Scenario B: mouseleave / mouseout:

  1. Add a mouseleave or mouseoout event to an element
  2. Add a button with a click handler outside the element from step 1
  3. Tap on the element.
  4. Tap on the button.

What is expected?

The click event should fire on the first tap. This is what happens in Vue 2.5.17 and 2.5.22.

What is actually happening?

The click event requires a second tap in order to trigger. This only happens in Vue 2.6.x.


I attempted to create a fiddle to reproduce this, but it works properly in the fiddle: https://jsfiddle.net/nathantreid/twcqg9jh/2/
My GitHub reproduction was created using the vue-cli.

Most helpful comment

Thanks, I had read the notes but wasn't sure if that was what caused the issue and if it was expected behavior. .capture doesn't work in this case. However, thanks to your confirmation that this is caused by switching back to microtasks and after learning that iOS Safari suppresses click events when mouseenter/mouseleave changes the DOM, I was able to work around the issue by using setTimeout to schedule the DOM modifications caused by mouseenter/mouseleave as a separate task, e.g.:

<div
      @mouseenter="handleMouseEvent('mouseenter')"
      @mouseleave="handleMouseEvent('mouseleave')"
    >
      <button @click="count++">
        Increment button inside mouseenter / mouseleave
      </button>
    </div>
methods: {
    handleMouseEvent(eventName) {
      setTimeout(() => {
        // Code that changes the DOM must go here
        this.debugLog.push(eventName);
      }, 0);
    },
  },

All 4 comments

this is due to the changes in micro/macro task in 2.6 (make sure to check the release notes)
You should be able to use a .capture modifier to correctly trap the event

Thanks, I had read the notes but wasn't sure if that was what caused the issue and if it was expected behavior. .capture doesn't work in this case. However, thanks to your confirmation that this is caused by switching back to microtasks and after learning that iOS Safari suppresses click events when mouseenter/mouseleave changes the DOM, I was able to work around the issue by using setTimeout to schedule the DOM modifications caused by mouseenter/mouseleave as a separate task, e.g.:

<div
      @mouseenter="handleMouseEvent('mouseenter')"
      @mouseleave="handleMouseEvent('mouseleave')"
    >
      <button @click="count++">
        Increment button inside mouseenter / mouseleave
      </button>
    </div>
methods: {
    handleMouseEvent(eventName) {
      setTimeout(() => {
        // Code that changes the DOM must go here
        this.debugLog.push(eventName);
      }, 0);
    },
  },

@nathantreid thanks fro the extra info, you just saved me from a deep dark hole 馃檹

just stumbled upon this in iOS13 (!), as in iOS 12 and before everything worked fine.. Looks like setTimeout cannot be 0 to handle this, I set to 100 ms and it works fine now. Kudos to @nathantreid!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

6pm picture 6pm  路  3Comments

bfis picture bfis  路  3Comments

finico picture finico  路  3Comments

seemsindie picture seemsindie  路  3Comments

franciscolourenco picture franciscolourenco  路  3Comments