134

Is it possible to implement "long press" in JavaScript (or jQuery)? How?

alt text
(source: androinica.com)

HTML

<a href="" title="">Long press</a>

JavaScript

$("a").mouseup(function(){
  // Clear timeout
  return false;
}).mousedown(function(){
  // Set timeout
  return false; 
});
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Randy Mayer
  • 8,455
  • 8
  • 25
  • 11
  • 7
    I'd probably create custom jQuery event using your code as a base, so you can just do `jQuery(...).longclick(function() { ... });` – Matti Virkkunen Apr 12 '10 at 20:39
  • 3
    The question is not tagged with jQuery, although it should be. The question asks first for a pure Javascript solution, which I prefer, or optionally (in parenthesis), a jQuery solution. Most answers seem to default to jQuery as a standard assumption. I have always despised jQuery and never once used it nor felt any compelling need for it. Some enjoy using it, that is fine, to each their own. Answers using either technique hurt nothing. But since the question will accept jQuery solutions, a jQuery tag might get more eyeballs and hopefully better answers. The jQuery answers here seem lackluster. –  Mar 29 '19 at 13:44

20 Answers20

180

There is no 'jQuery' magic, just JavaScript timers.

var pressTimer;

$("a").mouseup(function(){
  clearTimeout(pressTimer);
  // Clear timeout
  return false;
}).mousedown(function(){
  // Set timeout
  pressTimer = window.setTimeout(function() { ... Your Code ...},1000);
  return false; 
});
M.M
  • 2,254
  • 1
  • 20
  • 33
Diodeus - James MacFarlane
  • 112,730
  • 33
  • 157
  • 176
  • Couldn't have said it better myself. – mattbasta Apr 12 '10 at 20:34
  • 2
    Also... You need to specify $("a").click(function(){ return false; }); – Randy Mayer Apr 12 '10 at 20:39
  • 49
    Wouldn't this fire on a drag as well? – Gallal Aug 09 '12 at 17:18
  • I tried to add this code to a the rendering div of a jQuery Mobile Flip Switch, which broke that switch. – Angelo.Hannes Nov 08 '12 at 09:19
  • 16
    @Gallal Presumably it would be fairly simple to see to that by calling `clearTimeout(pressTimer)` on `mousemove`, unless I'm missing something. Which admittedly would hardly be unprecendented. – David John Welsh Dec 19 '13 at 05:19
  • 9
    @DavidJohnWelsh Just what I've been looking at, you don't just want mouse move though - holding you finger dead steady and not moving 1px is quite hard! You need to apply a threshold (if mouse hasn't moved 10px) etc. Gets complicated quite quickly! – Ian Feb 18 '15 at 12:44
  • 8
    Bear in mind that if you're expecting this to work on phones, they often have their own default longpress behaviour (chrome on android, for example, shows a modal menu with various options when you long press a link). I didn't have a lot of luck preventing this, and to be honest interfering with browser default behaviour is a hiding to nothing anyway. – dartacus Nov 02 '16 at 12:49
  • 1
    I had to add a mouseout as well to clear the timeout, so that this only triggers if the press remains within the component's boundaries. – Smig Jul 04 '17 at 02:58
  • May a ask what is the purpose of 'return false' statement here for each function? – chen.w Nov 15 '18 at 16:14
  • 6
    Although this is the selected answer, it is not really answering the question. It is overly simplistic and naive. Any long press event must address multiple issues which this answer ignores. 1) Distinguish long press from drag from gesture from multi touch (i.e. pinch zoom in or out) 2) Cancel if movement outside of element or browser area 3) Address default behavior of text selection on a significant number of platforms and devices 4) Allow a configurable threshhold for sensitivity and not rely upon magic numbers. Particularly helpful for - but not exclusive to - accessibility concerns. –  Mar 29 '19 at 11:59
  • 1
    Please check how it is implemented with handling edge cases in prod - https://docs.sencha.com/touch/2.1.1/source/LongPress.html#Ext-dom-Element-event-longpress – Nazar Vynnytskyi Aug 29 '20 at 06:19
36

Based on Maycow Moura's answer, I wrote this. It also ensures that the user didn't do a right click, which would trigger a long press and works on mobile devices. DEMO

var node = document.getElementsByTagName("p")[0];
var longpress = false;
var presstimer = null;
var longtarget = null;

var cancel = function(e) {
    if (presstimer !== null) {
        clearTimeout(presstimer);
        presstimer = null;
    }

    this.classList.remove("longpress");
};

var click = function(e) {
    if (presstimer !== null) {
        clearTimeout(presstimer);
        presstimer = null;
    }

    this.classList.remove("longpress");

    if (longpress) {
        return false;
    }

    alert("press");
};

var start = function(e) {
    console.log(e);

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

    longpress = false;

    this.classList.add("longpress");

    if (presstimer === null) {
        presstimer = setTimeout(function() {
            alert("long click");
            longpress = true;
        }, 1000);
    }

    return false;
};

node.addEventListener("mousedown", start);
node.addEventListener("touchstart", start);
node.addEventListener("click", click);
node.addEventListener("mouseout", cancel);
node.addEventListener("touchend", cancel);
node.addEventListener("touchleave", cancel);
node.addEventListener("touchcancel", cancel);

You should also include some indicator using CSS animations:

p {
    background: red;
    padding: 100px;
}

.longpress {
    -webkit-animation: 1s longpress;
            animation: 1s longpress;
}

@-webkit-keyframes longpress {
    0%, 20% { background: red; }
    100% { background: yellow; }
}

@keyframes longpress {
    0%, 20% { background: red; }
    100% { background: yellow; }
}
kelunik
  • 6,750
  • 2
  • 41
  • 70
  • I made this modified version, to do something constantly while the button is held down [jsfiddle](http://jsfiddle.net/s77vsLxp/) but for some reason on Android it runs even after you stop touching the + button... – Xander Sep 01 '15 at 14:52
  • @Xander: Maybe because the `:hover` state is sticky on touch devices, maybe that also applies here. – kelunik Sep 01 '15 at 15:03
  • Dang, I wonder if there's any way to get -/+ number increment buttons working on a mobile site that support long presses. Every method I find only supports having to click repeatedly which is a pain for huge numbers. Thanks though! – Xander Sep 01 '15 at 15:10
  • @Xander: Actually, `touchend` should fire IMO, there's no reason to have it sticky when it's special code for touch devices, maybe I'll try something tomorrow. – kelunik Sep 01 '15 at 16:30
  • 1
    Figured out the issue on Android. Pressing fires both mousedown and touchstart so it had 2 timers running but only 1 being cancelled by lifting your finger. Wrapped presstimer with if (presstimer === null) to make sure timer wasn't already active. – Xander Sep 01 '15 at 16:32
  • @Xander: Makes sense, could you edit the answer and add a note? – kelunik Sep 01 '15 at 16:33
  • @Xander: presstimer should be set to null, after clearTimeout – SuperNova Dec 07 '15 at 12:07
  • this is the best solution I have found so far. because it does not relay on any plugin or external library. It is just missing `node.addEventListener("mouseup", cancel);` when you need to increase some variable \ DOM element until the user leaves the mouse button. – Jonathan Applebaum Sep 17 '18 at 18:33
  • There are two problems with this technique: The timers are not cancelled if this is accompanied by a move event (drag element, or mobile gesture or moving out of element or off page to user interface or screen area in case of a windowed app mobile configuration), or if there are multiple touch points (combined with press and drag, i.e. pinch-zoom in or out). Also this doesn't seem to help Android/Chrome's (maybe others' too) penchant for going into text select mode on long press. Until or unless long-press is formally added to DOM Events spec, expect horrible bugs. –  Mar 29 '19 at 11:47
27

You can use taphold event of jQuery mobile API.

jQuery("a").on("taphold", function( event ) { ... } )
doganak
  • 798
  • 14
  • 31
  • 3
    Please note: jquery mobile conflicts with jquery ui. See also http://stackoverflow.com/questions/24379514/how-to-resolve-the-conflict-between-jquery-ui-and-jquery-mobile – Marcel Verwey Mar 19 '16 at 13:45
23

I created long-press-event (0.5k pure JS) to solve this, it adds a long-press event to the DOM.

Listen for a long-press on any element:

// the event bubbles, so you can listen at the root level
document.addEventListener('long-press', function(e) {
  console.log(e.target);
});

Listen for a long-press on a specific element:

// get the element
var el = document.getElementById('idOfElement');

// add a long-press event listener
el.addEventListener('long-press', function(e) {

    // stop the event from bubbling up
    e.preventDefault()

    console.log(e.target);
});

Works in IE9+, Chrome, Firefox, Safari & hybrid mobile apps (Cordova & Ionic on iOS/Android)

Demo

John Doherty
  • 3,669
  • 36
  • 38
  • 2
    Owesome, mate !! – Jeff T. Dec 05 '18 at 08:48
  • 3
    This solution monkey patches the window.CustomEvent object in a somewhat haphazard, incomplete and non-standard way. It does not properly create read-only properties as read-only but rather read-write. It is specifically missing returnValue, type, timeStamp and isTrusted. It does not address drag, gesture, pinch zoom in or out, or multi-touch misfires of long press, nor does it address the issue of a large number of devices and/or platforms which default long press to text selection even at 500ms. The library is missing any and all test cases for these conditions. –  Mar 29 '19 at 14:16
  • 15
    It’s Open Source, feel free to contribute to the project :) – John Doherty Mar 29 '19 at 15:21
  • @JohnDoherty great! but can we still use "onClick" with the same element? – Devashish Jun 09 '19 at 07:02
  • 3
    You should still get the 'onclick' event so long as the long press is released before 'long-press-delay' timer kicks in – John Doherty Jun 10 '19 at 02:03
  • If you want to disable click during long press, read this : https://stackoverflow.com/questions/56802461/long-press-also-fires-click-and-mouseup – fred727 Jun 05 '20 at 16:50
14

While it does look simple enough to implement on your own with a timeout and a couple of mouse event handlers, it gets a bit more complicated when you consider cases like click-drag-release, supporting both press and long-press on the same element, and working with touch devices like the iPad. I ended up using the longclick jQuery plugin (Github), which takes care of that stuff for me. If you only need to support touchscreen devices like mobile phones, you might also try the jQuery Mobile taphold event.

filoxo
  • 8,132
  • 3
  • 33
  • 38
ʇsәɹoɈ
  • 22,757
  • 7
  • 55
  • 61
  • 1
    The Github link works, but the project hasn't been updated since 2010 and doesn't work with current jquery versions. However, replacing handle.apply with dispatch.apply in the source code fixes it. – arlomedia Nov 04 '16 at 17:27
  • This fork contains the fix referenced by arlomedia https://github.com/jgarnet/Longclick/commit/085561d851a9148b5584b5665649f662babbe029 – Andy Gee Jun 29 '22 at 11:00
12

For modern, mobile browsers:

document.addEventListener('contextmenu', callback);

https://developer.mozilla.org/en-US/docs/Web/Events/contextmenu

Kory Nunn
  • 273
  • 3
  • 5
11

jQuery plugin. Just put $(expression).longClick(function() { <your code here> });. Second parameter is hold duration; default timeout is 500 ms.

(function($) {
    $.fn.longClick = function(callback, timeout) {
        var timer;
        timeout = timeout || 500;
        $(this).mousedown(function() {
            timer = setTimeout(function() { callback(); }, timeout);
            return false;
        });
        $(document).mouseup(function() {
            clearTimeout(timer);
            return false;
        });
    };

})(jQuery);
Mihai Iorga
  • 39,330
  • 16
  • 106
  • 107
piwko28
  • 119
  • 1
  • 2
6
$(document).ready(function () {
    var longpress = false;

    $("button").on('click', function () {
        (longpress) ? alert("Long Press") : alert("Short Press");
    });

    var startTime, endTime;
    $("button").on('mousedown', function () {
        startTime = new Date().getTime();
    });

    $("button").on('mouseup', function () {
        endTime = new Date().getTime();
        longpress = (endTime - startTime < 500) ? false : true;
    });
});

DEMO

razz
  • 9,770
  • 7
  • 50
  • 68
  • 2
    Whit this code the longclick is not fired at the end of 500ms. The user can die clicking on the mouse :). The long click is fired only if the user stop to click on the button. – jedi May 16 '16 at 12:57
  • would this cover the case when a user started scrolling instead of finishing their longpress in the same spot? – Akin Hwan May 31 '19 at 13:06
  • @AkinHwan No it would only get triggered if the mouse click was released over the same element. – razz May 31 '19 at 18:21
5

For cross platform developers (Note All answers given so far will not work on iOS):

Mouseup/down seemed to work okay on android - but not all devices ie (samsung tab4). Did not work at all on iOS.

Further research its seems that this is due to the element having selection and the native magnification interupts the listener.

This event listener enables a thumbnail image to be opened in a bootstrap modal, if the user holds the image for 500ms.

It uses a responsive image class therefore showing a larger version of the image. This piece of code has been fully tested upon (iPad/Tab4/TabA/Galaxy4):

var pressTimer;  
$(".thumbnail").on('touchend', function (e) {
   clearTimeout(pressTimer);
}).on('touchstart', function (e) {
   var target = $(e.currentTarget);
   var imagePath = target.find('img').attr('src');
   var title = target.find('.myCaption:visible').first().text();
   $('#dds-modal-title').text(title);
   $('#dds-modal-img').attr('src', imagePath);
   // Set timeout
   pressTimer = window.setTimeout(function () {
      $('#dds-modal').modal('show');
   }, 500)
});
tyler_mitchell
  • 1,727
  • 1
  • 19
  • 27
  • nice solution for iOS – eric xu Jun 03 '17 at 10:25
  • how would I prevent touches that start on the thumbnail, but say end up being a scroll. in other words, not a touchstart/end in place, but a touch that started on the element with handler, but ends up being a scroll – Akin Hwan May 31 '19 at 13:01
3

The Diodeus's answer is awesome, but it prevent you to add a onClick function, it'll never run hold function if you put an onclick. And the Razzak's answer is almost perfect, but it run hold function only on mouseup, and generally, the function runs even if user keep holding.

So, I joined both, and made this:

$(element).on('click', function () {
    if(longpress) { // if detect hold, stop onclick function
        return false;
    };
});

$(element).on('mousedown', function () {
    longpress = false; //longpress is false initially
    pressTimer = window.setTimeout(function(){
    // your code here

    longpress = true; //if run hold function, longpress is true
    },1000)
});

$(element).on('mouseup', function () {
    clearTimeout(pressTimer); //clear time on mouseup
});
Sven Kannenberg
  • 859
  • 2
  • 10
  • 20
Maycow Moura
  • 6,471
  • 2
  • 22
  • 19
1

You could set the timeout for that element on mouse down and clear it on mouse up:

$("a").mousedown(function() {
    // set timeout for this element
    var timeout = window.setTimeout(function() { /* … */ }, 1234);
    $(this).mouseup(function() {
        // clear timeout for this element
        window.clearTimeout(timeout);
        // reset mouse up event handler
        $(this).unbind("mouseup");
        return false;
    });
    return false;
});

With this each element gets its own timeout.

Gumbo
  • 643,351
  • 109
  • 780
  • 844
1

This worked for me:

const a = document.querySelector('a');

a.oncontextmenu = function() {
   console.log('south north');
};

https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/oncontextmenu

Zombo
  • 1
  • 62
  • 391
  • 407
0

You can use jquery-mobile's taphold. Include the jquery-mobile.js and the following code will work fine

$(document).on("pagecreate","#pagename",function(){
  $("p").on("taphold",function(){
   $(this).hide(); //your code
  });    
});
Prashant_M
  • 2,868
  • 1
  • 31
  • 24
  • This should be the accepted answer since jquery-mobile provides a good stable framework – pasx Jul 30 '15 at 05:17
0

Most elegant and clean is a jQuery plugin: https://github.com/untill/jquery.longclick/, also available as packacke: https://www.npmjs.com/package/jquery.longclick.

In short, you use it like so:

$( 'button').mayTriggerLongClicks().on( 'longClick', function() { your code here } );

The advantage of this plugin is that, in contrast to some of the other answers here, click events are still possible. Note also that a long click occurs, just like a long tap on a device, before mouseup. So, that's a feature.

untill
  • 1,513
  • 16
  • 20
0

I needed something for longpress keyboard events, so I wrote this.

var longpressKeys = [13];
var longpressTimeout = 1500;
var longpressActive = false;
var longpressFunc = null;

document.addEventListener('keydown', function(e) {
    if (longpressFunc == null && longpressKeys.indexOf(e.keyCode) > -1) {
        longpressFunc = setTimeout(function() {
            console.log('longpress triggered');
            longpressActive = true;
        }, longpressTimeout);

    // any key not defined as a longpress
    } else if (longpressKeys.indexOf(e.keyCode) == -1) {
        console.log('shortpress triggered');
    }
});

document.addEventListener('keyup', function(e) {
    clearTimeout(longpressFunc);
    longpressFunc = null;

    // longpress key triggered as a shortpress
    if (!longpressActive && longpressKeys.indexOf(e.keyCode) > -1) {
        console.log('shortpress triggered');
    }
    longpressActive = false;
});
Brad.Smith
  • 1,071
  • 3
  • 14
  • 28
0

In vanila JS if need to detect long-click after click released:

    document.addEventListener("mousedown", longClickHandler, true);
    document.addEventListener("mouseup", longClickHandler, true);

    let startClick = 0;
    function longClickHandler(e){   
      if(e.type == "mousedown"){
        startClick = e.timeStamp;
      }
      else if(e.type == "mouseup" && startClick > 0){
        if(e.timeStamp - startClick > 500){  // 0.5 secound
          console.log("Long click !!!");
        }
      }
    }

May need to use timer if need to check long-click while clicking. But for most case after release click is enought.

Zorro
  • 1,085
  • 12
  • 19
-1

For me it's work with that code (with jQuery):

var int       = null,
    fired     = false;

var longclickFilm = function($t) {
        $body.css('background', 'red');
    },
    clickFilm = function($t) {
        $t  = $t.clone(false, false);
        var $to = $('footer > div:first');
        $to.find('.empty').remove();
        $t.appendTo($to);
    },
    touchStartFilm = function(event) {
        event.preventDefault();
        fired     = false;
        int       = setTimeout(function($t) {
            longclickFilm($t);
            fired = true;
        }, 2000, $(this)); // 2 sec for long click ?
        return false;
    },
    touchEndFilm = function(event) {
        event.preventDefault();
        clearTimeout(int);
        if (fired) return false;
        else  clickFilm($(this));
        return false;
    };

$('ul#thelist .thumbBox')
    .live('mousedown touchstart', touchStartFilm)
    .live('mouseup touchend touchcancel', touchEndFilm);
molokoloco
  • 4,504
  • 2
  • 33
  • 27
-1

You can check the time to identify Click or Long Press [jQuery]

function AddButtonEventListener() {
try {
    var mousedowntime;
    var presstime;
    $("button[id$='" + buttonID + "']").mousedown(function() {
        var d = new Date();
        mousedowntime = d.getTime();
    });
    $("button[id$='" + buttonID + "']").mouseup(function() {
        var d = new Date();
        presstime = d.getTime() - mousedowntime;
        if (presstime > 999/*You can decide the time*/) {
            //Do_Action_Long_Press_Event();
        }
        else {
            //Do_Action_Click_Event();
        }
    });
}
catch (err) {
    alert(err.message);
}
} 
Derin
  • 1,202
  • 1
  • 15
  • 25
-1

like this?

target.addEeventListener("touchstart", function(){
   // your code ...
}, false);    
Zombo
  • 1
  • 62
  • 391
  • 407
翁沈顺
  • 21
  • 1
-1

You can use jquery Touch events. (see here)

  let holdBtn = $('#holdBtn')
  let holdDuration = 1000
  let holdTimer

  holdBtn.on('touchend', function () {
    // finish hold
  });
  holdBtn.on('touchstart', function () {
    // start hold
    holdTimer = setTimeout(function() {
      //action after certain time of hold
    }, holdDuration );
  });
Irteza Asad
  • 1,145
  • 12
  • 4