Long Press em JavaScript?

117

É possível implementar "long press" em JavaScript (ou jQuery)? Quão?

texto alternativo
(fonte: androinica.com )

HTML

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

JavaScript

$("a").mouseup(function(){
  // Clear timeout
  return false;
}).mousedown(function(){
  // Set timeout
  return false; 
});
Randy Mayer
fonte
7
Eu provavelmente criaria um evento jQuery personalizado usando seu código como base, então você pode simplesmente fazerjQuery(...).longclick(function() { ... });
Matti Virkkunen
1
A questão não está marcada com jQuery, embora devesse ser. A questão pede primeiro uma solução Javascript puro, que eu prefiro, ou opcionalmente (entre parênteses), uma solução jQuery. A maioria das respostas parece ter como padrão jQuery como uma suposição padrão. Sempre desprezei o jQuery e nunca o usei nem senti qualquer necessidade urgente por ele. Alguns gostam de usá-lo, isso é bom, para cada um deles. Respostas usando qualquer uma das técnicas não prejudicam nada. Mas, uma vez que a pergunta aceitará soluções jQuery, uma tag jQuery pode atrair mais olhos e, com sorte, respostas melhores. As respostas do jQuery aqui parecem sem brilho.

Respostas:

159

Não existe mágica 'jQuery', apenas timers JavaScript.

var pressTimer;

$("a").mouseup(function(){
  clearTimeout(pressTimer);
  // Clear timeout
  return false;
}).mousedown(function(){
  // Set timeout
  pressTimer = window.setTimeout(function() { ... Your Code ...},1000);
  return false; 
});
Diodeus - James MacFarlane
fonte
39
Isso não atiraria em uma drag também?
Gallal
11
@Gallal Presumivelmente, seria bastante simples para ver de que chamando clearTimeout(pressTimer)on mousemove, a menos que eu estou faltando alguma coisa. O que dificilmente seria sem precedentes.
David John Welsh
5
@DavidJohnWelsh Exatamente o que tenho visto, você não quer apenas mover o mouse - segurar seu dedo com firmeza e não se mover 1px é bem difícil! Você precisa aplicar um limite (se o mouse não tiver movido 10px) etc. Fica complicado rapidamente!
Ian
6
Lembre-se de que, se você espera que isso funcione em telefones, eles geralmente têm seu próprio comportamento longpress padrão (o Chrome no Android, por exemplo, mostra um menu modal com várias opções quando você pressiona um link por alguns segundos). Eu não tive muita sorte em evitar isso e, para ser honesto, interferir no comportamento padrão do navegador é uma forma de esconder nada.
dartacus
4
Embora esta seja a resposta selecionada, na verdade não está respondendo à pergunta. É extremamente simplista e ingênuo. Qualquer evento de toque longo deve abordar vários problemas que esta resposta ignora. 1) Distinguir pressionamento longo de arrastar, gesto de multitoque (ou seja, aproximar ou afastar o zoom) 2) Cancelar se houver movimento fora do elemento ou da área do navegador 3) Endereço padrão de comportamento de seleção de texto em um número significativo de plataformas e dispositivos 4) Permitir um limite configurável para sensibilidade e não depende de números mágicos. Particularmente útil para - mas não exclusivo - questões de acessibilidade.
34

Com base na resposta de Maycow Moura, eu escrevi isso. Também garante que o usuário não faça um clique com o botão direito, o que acionaria um toque longo e funciona em dispositivos móveis. 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);

Você também deve incluir alguns indicadores usando animações CSS:

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
fonte
Fiz esta versão modificada, para fazer algo constantemente enquanto o botão é pressionado jsfiddle, mas por alguma razão no Android ele roda mesmo depois de você parar de tocar no botão + ...
Xander
@Xander: Talvez porque o :hoverestado seja pegajoso em dispositivos de toque, talvez isso também se aplique aqui.
kelunik 01 de
Droga, eu me pergunto se há alguma maneira de fazer com que os botões - / + de incremento numérico funcionem em um site móvel que suporte pressionamentos longos. Todos os métodos que encontro suportam apenas clicar repetidamente, o que é uma dor para números enormes. Obrigado!
Xander
@Xander: Na verdade, touchenddeveria disparar IMO, não há razão para mantê- lo pegajoso quando é um código especial para dispositivos de toque, talvez eu tente algo amanhã.
Kelunik 01 de
1
Descobri o problema no Android. Pressionar dispara o mouse para baixo e o touchstart, de forma que 2 temporizadores estão em execução, mas apenas 1 é cancelado levantando o dedo. Presstimer envolvido com if (presstimer === null) para garantir que o temporizador já não esteja ativo.
Xander
25

Você pode usar o evento taphold da API móvel jQuery.

jQuery("a").on("taphold", function( event ) { ... } )
Doganak
fonte
2
Observação: o jquery mobile está em conflito com o jquery ui. Consulte também stackoverflow.com/questions/24379514/…
Marcel Verwey
16

Criei evento de pressionamento longo (0,5k JavaScript puro) para resolver isso, ele adiciona umlong-press evento ao DOM.

Ouça um long-pressem qualquer elemento:

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

Ouça um long-pressem um elemento específico :

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

Funciona no IE9 +, Chrome, Firefox, Safari e aplicativos móveis híbridos (Cordova e Ionic no iOS / Android)

Demo

John Doherty
fonte
2
Legal, cara !!
Jeff T.
1
Este macaco de solução corrige o objeto window.CustomEvent de uma forma um tanto desordenada, incompleta e fora do padrão. Ele não cria propriedades somente leitura como somente leitura, mas sim como leitura e gravação. Faltam especificamente returnValue, type, timeStamp e isTrusted. Não aborda o arrasto, gesto, apertar zoom in ou out, ou falhas de disparo multitoque do pressionamento longo, nem aborda o problema de um grande número de dispositivos e / ou plataformas que padronizam o pressionamento longo para seleção de texto, mesmo a 500 ms. A biblioteca está faltando todos e quaisquer casos de teste para essas condições.
4
É Open Source, fique à vontade para contribuir com o projeto :)
John Doherty
@JohnDoherty ótimo! mas ainda podemos usar "onClick" com o mesmo elemento?
Devashish
2
Você ainda deve obter o evento 'onclick', contanto que o pressionamento longo seja liberado antes que o cronômetro de 'atraso pressionado' comece
John Doherty
15

Embora pareça simples o suficiente para implementar sozinho com um tempo limite e alguns manipuladores de eventos do mouse, fica um pouco mais complicado quando você considera casos como clicar e arrastar e soltar, suportando tanto pressionar quanto pressionar longamente no mesmo elemento e trabalhar com dispositivos de toque como o iPad. Acabei usando o plugin jQuery longclick ( Github ), que cuida disso para mim. Se você só precisa oferecer suporte a dispositivos com tela de toque, como telefones celulares, também pode experimentar o evento jQuery Mobile taphold .

ʇsәɹoɈ
fonte
O link do Github funciona, mas o projeto não é atualizado desde 2010 e não funciona com as versões atuais do jquery. No entanto, substituir handle.apply por dispatch.apply no código-fonte corrige o problema.
arlomedia
11

plugin jQuery. Basta colocar $(expression).longClick(function() { <your code here> });. O segundo parâmetro é a duração da espera; o tempo limite padrão é 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);
piwko28
fonte
isso não é retido na chamada.
Campeão
oi
mano
6

Para desenvolvedores de plataforma cruzada (Observação: todas as respostas fornecidas até agora não funcionarão no iOS) :

O mouse para cima / para baixo parecia funcionar bem no Android - mas nem todos os dispositivos, ou seja, (samsung tab4). Não funcionou em iOS .

Pesquisas posteriores parecem que isso se deve ao elemento ter seleção e a ampliação nativa interrompe o ouvinte.

Este ouvinte de eventos permite que uma imagem em miniatura seja aberta em um modal de bootstrap, se o usuário mantiver a imagem por 500 ms.

Ele usa uma classe de imagem responsiva, portanto, mostrando uma versão maior da imagem. Este pedaço de código foi totalmente testado em (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
fonte
boa solução para iOS
eric xu
como evitaria toques que começam na miniatura, mas digamos que acabam sendo um pergaminho. em outras palavras, não é um toque inicial / final no lugar, mas um toque que começou no elemento com manipulador, mas acaba sendo um pergaminho
Akin Hwan
5
$(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
fonte
2
Com este código, o longclick não é disparado ao final de 500ms. O usuário pode morrer clicando com o mouse :). O clique longo é disparado apenas se o usuário parar para clicar no botão.
jedi de
isso cobriria o caso em que um usuário começou a rolar em vez de terminar seu longpress no mesmo local?
Akin Hwan
@AkinHwan Não, ele só seria acionado se o clique do mouse fosse liberado sobre o mesmo elemento.
razz
4

A resposta do Diodeus é incrível, mas impede que você adicione uma função onClick, ele nunca executará a função hold se você colocar um onclick. E a resposta do Razzak é quase perfeita, mas ele executa a função hold apenas no mouseup e, geralmente, a função executa mesmo se o usuário continuar segurando.

Então, eu entrei em ambos e fiz isto:

$(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
});
Maycow Moura
fonte
e se o usuário começar a rolar após o botão do mouse e não tiver a intenção de fazer um longpress
Akin Hwan
4

Para navegadores modernos para celular:

document.addEventListener('contextmenu', callback);

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

Kory Nunn
fonte
Ou para JQuery, use $ (selector) .bind ('contextmenu', function () {})
dfmiller
pare de usar bind()jquery 1.7+ = on()and unbind()=off()
dbinott
2

Você pode definir o tempo limite para esse elemento ao pressionar o mouse e limpá-lo ao subir:

$("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;
});

Com isso, cada elemento obtém seu próprio tempo limite.

quiabo
fonte
1
$(this).mouseup(function(){});não remove o manipulador de eventos, ele adiciona outro. Use em seu .unbindlugar.
Matti Virkkunen
deve usar off()agora em vez de desvincular.
dbinott
1

Você pode usar o taphold do jquery-mobile. Inclua o jquery-mobile.js e o código a seguir funcionará bem

$(document).on("pagecreate","#pagename",function(){
  $("p").on("taphold",function(){
   $(this).hide(); //your code
  });    
});
Prashant_M
fonte
Esta deve ser a resposta aceita, já que o jquery-mobile fornece uma boa estrutura estável
pasx
1

Mais elegante e limpo é um plugin jQuery: https://github.com/untill/jquery.longclick/ , também disponível como packacke: https://www.npmjs.com/package/jquery.longclick .

Em suma, você o usa assim:

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

A vantagem deste plugin é que, ao contrário de algumas das outras respostas aqui, os eventos de clique ainda são possíveis. Observe também que um clique longo ocorre, assim como um toque longo em um dispositivo, antes do mouseup. Então, esse é um recurso.

até
fonte
0

Para mim é trabalhar com aquele código (com 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
fonte
0

Você pode verificar o tempo para identificar Click ou 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
fonte
0

como isso?

doc.addEeventListener("touchstart", function(){
    // your code ...
}, false);    
翁 沈 顺
fonte
0

Você pode usar jqueryeventos de toque. ( veja aqui )

  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
fonte
0

Eu precisava de algo para eventos de teclado longpress, então escrevi isso.

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
fonte
0

Acho que isso pode te ajudar:

var image_save_msg = 'You Can Not Save images!';
var no_menu_msg = 'Context Menu disabled!';
var smessage = "Content is protected !!";

function disableEnterKey(e) {
    if (e.ctrlKey) {
        var key;
        if (window.event)
            key = window.event.keyCode; //IE
        else
            key = e.which; //firefox (97)
        //if (key != 17) alert(key);
        if (key == 97 || key == 65 || key == 67 || key == 99 || key == 88 || key == 120 || key == 26 || key == 85 || key == 86 || key == 83 || key == 43) {
            show_wpcp_message('You are not allowed to copy content or view source');
            return false;
        } else
            return true;
    }
}

function disable_copy(e) {
    var elemtype = e.target.nodeName;
    var isSafari = /Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor);
    elemtype = elemtype.toUpperCase();
    var checker_IMG = '';
    if (elemtype == "IMG" && checker_IMG == 'checked' && e.detail >= 2) {
        show_wpcp_message(alertMsg_IMG);
        return false;
    }
    if (elemtype != "TEXT" && elemtype != "TEXTAREA" && elemtype != "INPUT" && elemtype != "PASSWORD" && elemtype != "SELECT" && elemtype != "OPTION" && elemtype != "EMBED") {
        if (smessage !== "" && e.detail == 2)
            show_wpcp_message(smessage);

        if (isSafari)
            return true;
        else
            return false;
    }
}

function disable_copy_ie() {
    var elemtype = window.event.srcElement.nodeName;
    elemtype = elemtype.toUpperCase();
    if (elemtype == "IMG") {
        show_wpcp_message(alertMsg_IMG);
        return false;
    }
    if (elemtype != "TEXT" && elemtype != "TEXTAREA" && elemtype != "INPUT" && elemtype != "PASSWORD" && elemtype != "SELECT" && elemtype != "OPTION" && elemtype != "EMBED") {
        //alert(navigator.userAgent.indexOf('MSIE'));
        //if (smessage !== "") show_wpcp_message(smessage);
        return false;
    }
}

function reEnable() {
    return true;
}
document.onkeydown = disableEnterKey;
document.onselectstart = disable_copy_ie;
if (navigator.userAgent.indexOf('MSIE') == -1) {
    document.onmousedown = disable_copy;
    document.onclick = reEnable;
}

function disableSelection(target) {
    //For IE This code will work
    if (typeof target.onselectstart != "undefined")
        target.onselectstart = disable_copy_ie;

    //For Firefox This code will work
    else if (typeof target.style.MozUserSelect != "undefined") {
        target.style.MozUserSelect = "none";
    }

    //All other  (ie: Opera) This code will work
    else
        target.onmousedown = function() {
            return false
        }
    target.style.cursor = "default";
}
// on_body_load

window.onload = function() {
    disableSelection(document.body);
};



// disable_Right_Click



document.ondragstart = function() {
    return false;
}

function nocontext(e) {
    return false;
}
document.oncontextmenu = nocontext;

Harvansh Sainy
fonte