Long Press in JavaScript?

200,752

Solution 1

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; 
});

Solution 2

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; }
}

Solution 3

You can use taphold event of jQuery mobile API.

jQuery("a").on("taphold", function( event ) { ... } )

Solution 4

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

Solution 5

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.

Share:
200,752
Randy Mayer
Author by

Randy Mayer

Updated on August 22, 2021

Comments

  • Randy Mayer
    Randy Mayer over 2 years

    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; 
    });
    
  • mattbasta
    mattbasta about 14 years
    Couldn't have said it better myself.
  • Randy Mayer
    Randy Mayer about 14 years
    Also... You need to specify $("a").click(function(){ return false; });
  • Matti Virkkunen
    Matti Virkkunen about 14 years
    $(this).mouseup(function(){}); does not remove the event handler, it adds another one. Use .unbind instead.
  • Gallal
    Gallal over 11 years
    Wouldn't this fire on a drag as well?
  • Angelo.Hannes
    Angelo.Hannes over 11 years
    I tried to add this code to a the rendering div of a jQuery Mobile Flip Switch, which broke that switch.
  • David John Welsh
    David John Welsh over 10 years
    @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.
  • Ian
    Ian about 9 years
    @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!
  • GajendraSinghParihar
    GajendraSinghParihar about 9 years
    this is not retained in call.
  • user2075328
    user2075328 almost 9 years
    hi Bro can we use it as a backbone event
  • pasx
    pasx almost 9 years
    This should be the accepted answer since jquery-mobile provides a good stable framework
  • Xander
    Xander over 8 years
    I made this modified version, to do something constantly while the button is held down jsfiddle but for some reason on Android it runs even after you stop touching the + button...
  • kelunik
    kelunik over 8 years
    @Xander: Maybe because the :hover state is sticky on touch devices, maybe that also applies here.
  • Xander
    Xander over 8 years
    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!
  • kelunik
    kelunik over 8 years
    @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.
  • Xander
    Xander over 8 years
    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.
  • kelunik
    kelunik over 8 years
    @Xander: Makes sense, could you edit the answer and add a note?
  • SuperNova
    SuperNova over 8 years
    @Xander: presstimer should be set to null, after clearTimeout
  • Marcel Verwey
    Marcel Verwey about 8 years
    Please note: jquery mobile conflicts with jquery ui. See also stackoverflow.com/questions/24379514/…
  • dfmiller
    dfmiller about 8 years
    Or for JQuery use $(selector).bind('contextmenu', function() {})
  • jedi
    jedi almost 8 years
    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.
  • dbinott
    dbinott almost 8 years
    should use off() now instead of unbind.
  • dbinott
    dbinott almost 8 years
    stop using bind() jquery 1.7+ = on() and unbind() = off()
  • dartacus
    dartacus over 7 years
    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.
  • arlomedia
    arlomedia over 7 years
    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.
  • eric xu
    eric xu almost 7 years
    nice solution for iOS
  • Smig
    Smig almost 7 years
    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.
  • Jonathan Applebaum
    Jonathan Applebaum over 5 years
    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.
  • chen.w
    chen.w over 5 years
    May a ask what is the purpose of 'return false' statement here for each function?
  • Jeff T.
    Jeff T. over 5 years
    Owesome, mate !!
  • Admin
    Admin about 5 years
    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.
  • Admin
    Admin about 5 years
    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.
  • Admin
    Admin about 5 years
    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.
  • John Doherty
    John Doherty about 5 years
    It’s Open Source, feel free to contribute to the project :)
  • Akin Hwan
    Akin Hwan almost 5 years
    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
    Akin Hwan almost 5 years
    what if user starting scrolling after mousedown, and wasn't intending to do a longpress
  • Akin Hwan
    Akin Hwan almost 5 years
    would this cover the case when a user started scrolling instead of finishing their longpress in the same spot?
  • razz
    razz almost 5 years
    @AkinHwan No it would only get triggered if the mouse click was released over the same element.
  • Devashish
    Devashish almost 5 years
    @JohnDoherty great! but can we still use "onClick" with the same element?
  • John Doherty
    John Doherty almost 5 years
    You should still get the 'onclick' event so long as the long press is released before 'long-press-delay' timer kicks in
  • fred727
    fred727 almost 4 years
    If you want to disable click during long press, read this : stackoverflow.com/questions/56802461/…
  • Nazar Vynnytskyi
    Nazar Vynnytskyi over 3 years
    Please check how it is implemented with handling edge cases in prod - docs.sencha.com/touch/2.1.1/source/…
  • Alex Hajnal
    Alex Hajnal over 2 years
    Or inline the HTML element: oncontextmenu="callback();" It's probably desirable to add e.g. event.preventDefault(); somewhere near the top of the callback function.