javascriptvue.js

How to trigger long-press (click-and-hold) on mobile devices with Vue / JavaScript


This is only on Mobile devices. It works as intended on desktop, so I'll omit all those styles and functionality.

I have a Vue-component like this:

Template

<div class="container">

  <div class="invisible-overlay-styles">
    <button
      v-if="! videoIsPlaying"
      @click="playVideo()"
      @mousedown="playVideo()"
    >
    </button>

    <a
      v-if="videoIsPlaying"
    >
    </a>
  </div> <!-- invisible-overlay-styles -->

  <video ... /> 

</div>

All the divs, buttons, anchors and videos are stacked on top of one-another, using display: block and position: absolute; width: 100%; etc. etc. etc.

Method

  playVideo(){
    console.log( 'Im playing!!' );
    if( this.player ){
      if( this.player.paused ){
        this.player.play()
      }
    }
  }

If i click it, it works as it should:

But if I click-and-hold, then neither @click nor @mousedown is triggered. This is both in:


So two questions:

  1. I get why @click isn't triggered, since that's a combination of @mousedown and @mouseup. But why doesn't @mousedown get triggered, when I long-press?
  2. How do I target long-press on mobile devices using Vue (or JavaScript)?

Solution

  • EDIT: Dec 2022

    PointerEvent is the recommended way now as it covers all devices:

    <button
      v-if="!videoIsPlaying"
      @pointerdown="playVideo()"
    ></button>
    

    To read a long-press:

    <template>
      <button
        @pointerdown="isHolding = true"
        @pointerup="isHolding = false"
      ></button>
    </template>
    
    <script setup>
    import { ref } from 'vue';
    
    const isHolding = ref(false);
    </script>
    

    ORIGINAL:

    You'll need to use either the TouchEvent API or the PointerEvent API.

    The browser compatibility for both is good:

    They each have similar events to the MouseEvent API, i.e. touchstart = mousedown.

    I've personally found TouchEvents better than PointerEvents, but, you might have to do some testing to see which is best in your case.


    So, as you can guess with Vue, you can use @touchstart:

    <button
        v-if="!videoIsPlaying"
        @click="playVideo()"
        @mousedown="playVideo()"
        @touchstart="playVideo()"
    />
    

    If you want to determine it as a long-press, you'll have to store a value on touchstart and change it on touchend:

    <button
        @touchstart="touching = true"
        @touchend="touching = false"
    />
    ...
    data() {
        return {
            touching: false
        }
    }