Building a long press Directive in Vue

Translator: OFED

Write a long-press instruction using Vue

Ever want to be able to trigger a feature in your Vue app just by pressing a button for a few seconds?

Ever want to create a button that can be pressed once to clear a single input (or held to clear all input)?

Thought about it? Great. Great minds think alike.

This article explains how to perform a function and erase input while pressing (or holding) a button.

First, I’ll show you how to use a pure JS implementation. A Vue directive is also created.

Please fasten your seat belt. The fun is coming.

The principle of

To achieve the long press, users need to press and hold the button for several seconds.

To simulate this effect in our code, we need to start a timer when the mouse “clicks” on the button to listen to how long the user has been pressing it, and execute the corresponding function if it takes longer than we expect.

Very simple! However, we need to know when the user is holding down the button.

How to implement

When the user clicks the button, two other events are triggered before clicking the event: mouseDown and MouseUp.

The MouseDown event is triggered when the user presses the button, and the Mouseup event is called when the user releases the button.

What we need to do is:

  1. mousedownWhen the event is triggered, the timer starts.
  2. Once themouseupIf the event is triggered 2 seconds before it is expected, the timer is cleared and the corresponding function is not executed. Think of it as a normal click event.

As long as the timer is not cleared within our preset time, that is, the mouseup event is not triggered — then it is safe to assume that the user does not have a release button. Therefore, it can be judged as a long press, and the associated function can be executed.

practice

Let’s dive into the code and do this.

First, we must define three things, namely:

  1. A variable is used to store timers.
  2. A start function that starts the timer.
  3. A cancel function that cancels timers.

variable

This variable is mainly used to hold the value of setTimeout so that we can cancel it when the mouseup event is triggered.

let pressTimer = null;
Copy the code

The reason we set the variable value to null is to check the value of the variable to see if there is currently a timer running before canceling.

Start the function

This function includes a setTimeout, which is a basic method in JavaScript that allows a function to be executed after a certain time.

Note that during the execution of the Click event, two additional events are emitted. But what we need to start the timer is the Mousedown event. If it’s just a click event, you don’t need to start a timer.

// Create timer (execute function after 1s)
let start = (e) = > {
    // If it is a click event, the timer is not started
    if (e.type === 'click'&& e.button ! = =0) {
        return;
    }
    Make sure there are no running timers before starting a timer
    if (pressTimer === null) {
        pressTimer = setTimeout((a)= > {
            // Execute mission!!
        }, 1000)}}Copy the code

Cancel the function

This function, as the name suggests, is used to cancel setTimeout for function creation.

To cancel setTimeout, you can use the clearTimeout method in JavaScript, which is mainly used to clear the timer set by the setTimeout() method.

Before using clearTimeout, you need to check whether the pressTimer variable is null. If it is not null, it means that a timer is running. Therefore, we need to clear it first and set the pressTimer variable to NULL.

let cancel = (e) = > {
    // Check if pressTimer is null
    if(pressTimer ! = =null) {
        clearTimeout(pressTimer)
        pressTimer = null}}Copy the code

This function is called once the mouseup event is triggered.

Set trigger

All that is left is to add the event listener to the button that you want to long press.

addEventListener("mousedown", start);
addEventListener("click", cancel);
Copy the code

The above code together looks like this:

// Define variables
let pressTimer = null;

// Create timer (execute function after 1 second)
let start = (e) = > {

    if (e.type === 'click'&& e.button ! = =0) {
        return;
    }

    if (pressTimer === null) {
        pressTimer = setTimeout((a)= > {

            // Execute mission!!

        }, 1000)}}// Stop the timer
let cancel = (e) = > {

    // Check if there is a running timer
    if( pressTimer ! = =null ) {
        clearTimeout(pressTimer);
        pressTimer = null; }}// Select the element with id longPressButton
let el = document.getElementById('longPressButton');

// Add event listeners
el.addEventListener("mousedown", start);

// Long press the event cancel to cancel the timer
el.addEventListener("click", cancel);
el.addEventListener("mouseout", cancel);
Copy the code

Wrap with Vue instructions

When creating a Vue directive, you can create global or local directives. In this article, we use global directives.

First, we must declare the name of the custom directive.

Vue.directive('longpress', {})Copy the code

This registers a global custom directive called V-Longpress.

Next, we add the bind hook function with arguments, which allows us to refer to the element bound by the instruction, get the value passed to the instruction, and identify the component used by the instruction.

Vue.directive('longpress', {
    bind: function(el, binding, vNode) {}})Copy the code

Next, we’ll add the code for the long-press function to bind.

Vue.directive('longpress', {
    bind: function(el, binding, vNode) {

        // Define variables
        let pressTimer = null;

        // Define the function handler
        // Create timer (execute function after 1 second)
        let start = (e) = > {

            if (e.type === 'click'&& e.button ! = =0) {
                return;
            }

            if (pressTimer === null) {
                pressTimer = setTimeout((a)= > {

                    // Execute mission!!

                }, 1000)}}// Cancel the timer
        let cancel = (e) = > {

            // Check if there is a running timer
            if( pressTimer ! = =null ) {
                clearTimeout(pressTimer);
                pressTimer = null; }}// Add event listeners
        el.addEventListener("mousedown", start);

        // Cancel the timer
        el.addEventListener("click", cancel);
        el.addEventListener("mouseout", cancel); }})Copy the code

Next, we need to add a function to run the method passed to the Longpress directive.

Vue.directive('longpress', {
    bind: function(el, binding, vNode) {

        // Define variables
        let pressTimer = null;

        // Define the function handler
        // Create timer (execute function after 1 second)
        let start = (e) = > {

            if (e.type === 'click'&& e.button ! = =0) {
                return;
            }

            if (pressTimer === null) {
                pressTimer = setTimeout((a)= > {
                    // Execute the function
                    handler();
                }, 1000)}}// Stop the timer
        let cancel = (e) = > {

            // Check if there is a running timer
            if( pressTimer ! = =null ) {
                clearTimeout(pressTimer);
                pressTimer = null; }}// Run the function
        const handler = (e) = > {
            // Executes the method passed to the instruction
            binding.value(e)
        }

        // Add event listeners
        el.addEventListener("mousedown", start);

        // Cancel the timer
        el.addEventListener("click", cancel);
        el.addEventListener("mouseout", cancel); }})Copy the code

This directive can now be used in Vue applications, unless the value passed by the user to the directive is not a function. Therefore, we need to provide feedback to users through warnings.

To provide feedback to the user, we added the following to bind:

// Make sure the supplied expression is a function
if (typeofbinding.value ! = ='function') {
    // Get the component name
    const compName = vNode.context.name;
    // Pass the warning to the console
    let warn = `[longpress:] provided expression '${binding.expression}' is not a function, but has to be `;
    if (compName) { warn += `Found in component '${compName}'` }

    console.warn(warn);
}
Copy the code

Finally, it would be great if this instruction also worked on touchscreen devices. Therefore, we added the TouchStart, TouchEnd, and TouchCancel event listeners.

The final code is as follows:

Vue.directive('longpress', {
    bind: function(el, binding, vNode) {

        // Make sure the supplied expression is a function
        if (typeofbinding.value ! = ='function') {
            // Get the component name
            const compName = vNode.context.name;
            // Pass the warning to the console
            let warn = `[longpress:] provided expression '${binding.expression}' is not a function, but has to be `;
            if (compName) { warn += `Found in component '${compName}'`}

            console.warn(warn);
        }

        // Define variables
        let pressTimer = null;

        // Define the function handler
        // Create timer (execute function after 1 second)
        let start = (e) = > {

            if (e.type === 'click'&& e.button ! = =0) {
                return;
            }

            if (pressTimer === null) {
                pressTimer = setTimeout((a)= > {
                    // Execute the function
                    handler();
                }, 1000)}}// Cancel the timer
        let cancel = (e) = > {

            // Check if the timer has a value
            if( pressTimer ! = =null ) {
                clearTimeout(pressTimer);
                pressTimer = null; }}// Run the function
        const handler = (e) = > {
            // Executes the method passed to the instruction
            binding.value(e)
        }

        // Add event listeners
        el.addEventListener("mousedown", start);
        el.addEventListener("touchstart", start);

        // Cancel the timer
        el.addEventListener("click", cancel);
        el.addEventListener("mouseout", cancel);
        el.addEventListener("touchend", cancel);
        el.addEventListener("touchcancel", cancel); }})Copy the code

Now available in Vue components:

<template>
    <div>
        <button v-longpress="incrementPlusTen" @click="incrementPlusOne">{{value}}</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            value: 10}},methods: {
        / / 1
        incrementPlusOne() {
            this.value++
        },
        / / add 10
        incrementPlusTen() {
            this.value += 10}}}</script>
Copy the code

If you want to know more about custom directives, the hook functions available, the parameters that can be passed to the hook function, and the function shorthand, refer to the @vuejs documentation for a good explanation.

Call it a day. Cheers!