Como diferenciar o evento de clique único e o evento de clique duplo?

116

Eu tenho um único botão em li com id "my_id". Anexei dois eventos jQuery com este elemento

1

$("#my_id").click(function() { 
    alert('single click');
});

2

$("#my_id").dblclick(function() {
    alert('double click');
});

Mas toda vez que me dá o single click

user426795
fonte
O clique único sempre terá que disparar, é impossível saber se um clique duplo vai ocorrer, a menos que ele realmente ocorra dentro do tempo de clique duplo especificado.
Pieter Germishuys,
7
Isso é possível, verifique minha resposta. Você deve aguardar alguns ms após o primeiro clique e verificar se há um novo clique até o horário especificado. Desta forma, poderá saber se se trata de um simples clique ou de um duplo clique.
Adrien Schuler,

Respostas:

65

Você precisa usar um tempo limite para verificar se há um outro clique após o primeiro clique.

Aqui está o truque :

// Author:  Jacek Becela
// Source:  http://gist.github.com/399624
// License: MIT

jQuery.fn.single_double_click = function(single_click_callback, double_click_callback, timeout) {
  return this.each(function(){
    var clicks = 0, self = this;
    jQuery(this).click(function(event){
      clicks++;
      if (clicks == 1) {
        setTimeout(function(){
          if(clicks == 1) {
            single_click_callback.call(self, event);
          } else {
            double_click_callback.call(self, event);
          }
          clicks = 0;
        }, timeout || 300);
      }
    });
  });
}

Uso:

$("button").single_double_click(function () {
  alert("Try double-clicking me!")
}, function () {
  alert("Double click detected, I'm hiding")
  $(this).hide()
})
<button>Click Me!</button>

EDITAR:

Conforme declarado abaixo, prefira usar o dblclickevento nativo : http://www.quirksmode.org/dom/events/click.html

Ou aquele fornecido pela jQuery: http://api.jquery.com/dblclick/

Adrien Schuler
fonte
14
Isso pode parecer funcionar bem em geral, mas o intervalo de tempo permitido entre os cliques (pelo qual dois cliques são interpretados como um clique duplo) não difere de acordo com a configuração do sistema? Provavelmente é mais seguro confiar no dblclickevento
Jon z
1
Sim, acho que você está certo, dblclické definitivamente o caminho a percorrer hoje. jQuery também lida com este evento: api.jquery.com/dblclick
Adrien Schuler
14
@AdrienSchuler - Do documento que você vincula: Não é aconselhável vincular manipuladores aos eventos click e dblclick para o mesmo elemento. A questão é sobre ter ambos .
Álvaro González
Quando event.detaildisponível, é a melhor maneira de detectar um clique duplo no manipulador de cliques. Veja esta resposta .
Carl G
Boa solução Adrien, preciso dela para um caso em que temos um evento de clique único e duplo em um objeto e apenas um deve ser disparado. Portanto, sua solução é perfeita para mim. Eu apenas melhorei um pouco para ter "isto" sob controle: playcode.io/492022
LOstBrOkenKeY
79

O comportamento do dblclickevento é explicado em Quirksmode .

A ordem dos eventos para um dblclické:

  1. mousedown
  2. mouseup
  3. clique
  4. mousedown
  5. mouseup
  6. clique
  7. dblclick

A única exceção a essa regra é (claro) o Internet Explorer com sua ordem personalizada de:

  1. mousedown
  2. mouseup
  3. clique
  4. mouseup
  5. dblclick

Como você pode ver, ouvir os dois eventos juntos no mesmo elemento resultará em chamadas extras para seu clickmanipulador.

Ryan
fonte
não tenho certeza dblclické mesmo confiável ... se eu clicar uma vez ... espere um ou dois segundos, depois clique novamente, recebo um dblclickevento no segundo clique!
Michael
Esses cliques longos são úteis, por exemplo, para renomear arquivos. Seria bom se houvesse um parâmetro como 'longClick' para distingui-lo.
PeterM,
1
Quando event.detaildisponível, é a melhor maneira de detectar um clique duplo no manipulador de cliques. Veja esta resposta .
Carl G
38

Em vez de utilizar mais estados ad-hoc e setTimeout, descobrimos que existe uma propriedade nativa chamada detailque você pode acessar a partir do eventobjeto!

element.onclick = event => {
   if (event.detail === 1) {
     // it was a single click
   } else if (event.detail === 2) {
     // it was a double click
   }
};

Navegadores modernos e até mesmo o IE-9 suportam :)

Fonte: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail

kyw
fonte
A melhor resposta :)
sk8terboi87 ツ
3
No caso de cliques duplos, um único clique (onde event.detail === 1) ainda será acionado. Veja meu violino ou este comentário neste tópico .
Fabien Snauwaert
Boa observação @FabienSnauwaert. Acho que não podemos vincular o manipulador de eventos de clique único e duplo a um elemento específico; o método acima é útil para determinar definitivamente um clique duplo sem recorrer a coisas como cronômetro.
kyw
Você ainda precisa de um cronômetro no primeiro clique para não disparar. Verifique minha resposta abaixo stackoverflow.com/questions/5497073/…
Apenas uma nota de acessibilidade: se um evento de clique for disparado por um teclado (por exemplo, quando um usuário guia para colocar o foco em um botão e pressiona Enter), o evento é gerado com a propriedade de detalhe como 0, fazendo algo como if(evt.detail !== 1) return;rejeitar o dobro acidental cliques bloquearão o acesso a esse botão para usuários sem o mouse.
Gristow
25

Uma função simples. Nenhum jquery ou outra estrutura é necessária. Passe suas funções como parâmetros

<div onclick="doubleclick(this, function(){alert('single')}, function(){alert('double')})">click me</div>
    <script>
        function doubleclick(el, onsingle, ondouble) {
            if (el.getAttribute("data-dblclick") == null) {
                el.setAttribute("data-dblclick", 1);
                setTimeout(function () {
                    if (el.getAttribute("data-dblclick") == 1) {
                        onsingle();
                    }
                    el.removeAttribute("data-dblclick");
                }, 300);
            } else {
                el.removeAttribute("data-dblclick");
                ondouble();
            }
        }
    </script>
Renzo Ciot
fonte
2
Acho que essa deve ser a melhor resposta. No entanto, tive que reduzir o tempo limite para 200 para que ele funcione no Windows IE8, mas esse valor pode ser dependente do sistema operacional e do usuário. E também salvou a referência do cronômetro para poder cancelar a execução do clique se um duplo clique for registrado.
xpereta
Obrigado por este trecho de código, ele funciona muito bem no Chrome e Firefox (embora o Chrome não precise desse hack para funcionar com dbl e single).
vencedor em
usar el.dataset.dblclickseria uma maneira melhor de fazer isso agora.
WORMSS
Quando event.detaildisponível, é a melhor maneira de detectar um clique duplo no manipulador de cliques. Veja esta resposta .
Carl G
12

Receio que o comportamento dependa do navegador:

Não é aconselhável vincular os manipuladores aos eventos click e dblclick para o mesmo elemento. A sequência de eventos disparados varia de navegador para navegador, alguns recebendo dois eventos de clique antes do dblclick e outros apenas um. A sensibilidade do clique duplo (tempo máximo entre os cliques detectado como um clique duplo) pode variar de acordo com o sistema operacional e o navegador, e geralmente é configurável pelo usuário.

http://api.jquery.com/dblclick/

Executando seu código no Firefox, o alert () no click()manipulador evita que você clique uma segunda vez. Se você remover esse alerta, obterá os dois eventos.

Álvaro González
fonte
Quando event.detaildisponível, é a melhor maneira de detectar um clique duplo no manipulador de cliques. Veja esta resposta .
Carl G
11

Bem, para clicar duas vezes (clique duas vezes), você deve primeiro clicar uma vez. O click()manipulador dispara em seu primeiro clique e, como o alerta é exibido, você não tem chance de dar o segundo clique para disparar o dblclick()manipulador.

Mude seus manipuladores para fazer algo diferente de um alert()e você verá o comportamento. (talvez mude a cor de fundo do elemento):

$("#my_id").click(function() { 
    $(this).css('backgroundColor', 'red')
});

$("#my_id").dblclick(function() {
    $(this).css('backgroundColor', 'green')
});
bradley.ayers
fonte
7

Esta resposta tornou-se obsoleta com o tempo, verifique a solução de @kyw.

Criei uma solução inspirada na essência postada por @AdrienSchuler. Use esta solução apenas quando quiser vincular um único clique E um clique duplo a um elemento. Caso contrário, recomendo usar o nativo clicke os dblclickouvintes.

Estas são as diferenças:

  • Vanillajs, sem dependências
  • Não espere setTimeoutpara manipular o manipulador de clique ou duplo clique
  • Ao clicar duas vezes nele, primeiro dispara o manipulador de clique e, em seguida, o manipulador

Javascript:

function makeDoubleClick(doubleClickCallback, singleClickCallback) {
    var clicks = 0, timeout;
    return function() {
        clicks++;
        if (clicks == 1) {
            singleClickCallback && singleClickCallback.apply(this, arguments);
            timeout = setTimeout(function() { clicks = 0; }, 400);
        } else {
            timeout && clearTimeout(timeout);
            doubleClickCallback && doubleClickCallback.apply(this, arguments);
            clicks = 0;
        }
    };
}

Uso:

var singleClick = function(){ console.log('single click') };
var doubleClick = function(){ console.log('double click') };
element.addEventListener('click', makeDoubleClick(doubleClick, singleClick));

Abaixo está o uso em um jsfiddle, o botão jQuery é o comportamento da resposta aceita.

jsfiddle

A1rPun
fonte
2
O código acima e a versão 'Vanilla' do seu violino não funcionam ... aqui está uma versão Vanilla corrigida: jsfiddle.net/jeum/cpbwx5vr/19
jeum
@jeum Ele ainda funciona para mim em todos os navegadores. O que "não funciona" e o que você espera? Parece que sua versão não está fazendo o que eu pretendia.
A1rPun
Fist Modifiquei minha versão (removi o fechamento duplo), tente a nova: jsfiddle.net/jeum/cpbwx5vr/20 O problema com seu violino (versão Vanilla) é que o clique duplo aciona o manipulador único: ele recupera um único + duplo para mim.
jeum
@jeum Esse dá um erro. Sim, minha resposta difere da aceita e de outras respostas aqui e também especifico essa diferença nesta linha: Dispara um clique, depois um duplo-clique (não vá direto para o duplo-clique)
A1rPun
1
Sim, erro novamente, então esta versão responde à pergunta principal: jsfiddle.net/jeum/cpbwx5vr/21 . Agora quanto à sua versão (é verdade que eu não tinha entendido), você notou que 'jQuery' não alcança o que você deseja em seu violino?
jeum
5

Outra solução Vanilla simples baseada na resposta A1rPun (veja seu violino para a solução jQuery, e ambas estão nesta ).

Parece que para NÃO acionar um gerenciador de clique único quando o usuário clica duas vezes, o gerenciador de clique único é necessariamente acionado após um atraso ...

var single = function(e){console.log('single')},
    double = function(e){console.log('double')};

var makeDoubleClick = function(e) {

  var clicks = 0,
      timeout;

  return function (e) {

    clicks++;

    if (clicks == 1) {
      timeout = setTimeout(function () {
        single(e);
        clicks = 0;
      }, 250);
    } else {
      clearTimeout(timeout);
      double(e);
      clicks = 0;
    }
  };
}
document.getElementById('btnVanilla').addEventListener('click', makeDoubleClick(), false);
jeum
fonte
3
Esta é realmente a melhor alternativa se você não quiser que o clique único seja disparado :)
A1rPun
Quando event.detaildisponível, é a melhor maneira de detectar um clique duplo no manipulador de cliques. Veja esta resposta .
Carl G
5

Aqui está uma alternativa ao código de jeum para um número arbitrário de eventos:

 var multiClickHandler = function (handlers, delay) {
    var clicks = 0, timeout, delay = delay || 250;
    return function (e) {
      clicks++;
      clearTimeout(timeout);
      timeout = setTimeout(function () {
        if(handlers[clicks]) handlers[clicks](e);
        clicks = 0;
      }, delay);
    };
  }

  cy.on('click', 'node', multiClickHandler({
    1: function(e){console.log('single clicked ', e.cyTarget.id())},
    2: function(e){console.log('double clicked ', e.cyTarget.id())},
    3: function(e){console.log('triple clicked ', e.cyTarget.id())},
    4: function(e){console.log('quadro clicked ', e.cyTarget.id())},
    // ...
  }, 300));

Necessário para um aplicativo cytoscape.js .

próximo
fonte
Agradável! também podemos declarar uma nova variável dentro da função retornada e atribuí-la com this, que é o elemento clicado, e passá-la para o manipulador (ao lado eou em vez dele).
HeyJude
5

Como diferenciar entre cliques únicos e cliques duplos em um mesmo elemento?

Se você não precisa misturá-los, você pode confiar em clicke dblclicke cada um fará o trabalho perfeitamente.

Surge um problema ao tentar misturá-los: um dblclickevento irá, na verdade, disparar um clickevento também , então você precisa determinar se um único clique é um clique único "independente" ou parte de um clique duplo.

Além disso: você não deve usar ambos clicke dblclickem um e o mesmo elemento :

Não é aconselhável vincular os manipuladores aos eventos click e dblclick para o mesmo elemento. A sequência de eventos disparados varia de navegador para navegador, alguns recebendo dois eventos de clique antes do dblclick e outros apenas um. A sensibilidade do clique duplo (tempo máximo entre os cliques detectado como um clique duplo) pode variar de acordo com o sistema operacional e o navegador, e geralmente é configurável pelo usuário.
Fonte: https://api.jquery.com/dblclick/

Agora vamos às boas notícias:

Você pode usar a detailpropriedade do evento para detectar o número de cliques relacionados ao evento. Isso torna os cliques duplos dentro do clickrazoavelmente fáceis de detectar.

O problema continua em detectar cliques únicos e se eles fazem parte de um clique duplo ou não. Para isso, voltamos a usar um cronômetro e setTimeout.

Empacotando tudo junto, com o uso de um atributo de dados (para evitar uma variável global) e sem a necessidade de contarmos nós mesmos, obtemos:

HTML:

<div class="clickit" style="font-size: 200%; margin: 2em; padding: 0.25em; background: orange;">Double click me</div>

<div id="log" style="background: #efefef;"></div>

JavaScript:

<script>
var clickTimeoutID;
$( document ).ready(function() {

    $( '.clickit' ).click( function( event ) {

        if ( event.originalEvent.detail === 1 ) {
            $( '#log' ).append( '(Event:) Single click event received.<br>' );

            /** Is this a true single click or it it a single click that's part of a double click?
             * The only way to find out is to wait it for either a specific amount of time or the `dblclick` event.
             **/
            clickTimeoutID = window.setTimeout(
                    function() {
                        $( '#log' ).append( 'USER BEHAVIOR: Single click detected.<br><br>' );
                    },
                    500 // how much time users have to perform the second click in a double click -- see accessibility note below.
                );

        } else if ( event.originalEvent.detail === 2 ) {
            $( '#log' ).append( '(Event:) Double click event received.<br>' );
            $( '#log' ).append( 'USER BEHAVIOR: Double click detected.<br>' );
            window.clearTimeout( clickTimeoutID ); // it's a dblclick, so cancel the single click behavior.
        } // triple, quadruple, etc. clicks are ignored.

    });

});
</script>

Demo:

JSfiddle


Observações sobre acessibilidade e velocidades de clique duplo:

  • Como afirma a Wikipedia, "O atraso máximo necessário para que dois cliques consecutivos sejam interpretados como um clique duplo não é padronizado."
  • Nenhuma maneira de detectar a velocidade de clique duplo do sistema no navegador.
  • Parece que o padrão é 500 ms e o intervalo de 100-900mms no Windows ( fonte )
  • Pense nas pessoas com deficiência que definem, nas configurações do sistema operacional, a velocidade do clique duplo para a mais lenta.
    • Se a velocidade de clique duplo do sistema for mais lenta do que nosso padrão 500 ms acima, os comportamentos de clique único e duplo serão acionados.
    • Não use depender de cliques simples e duplos combinados em um mesmo item.
    • Ou: adicione uma configuração nas opções para poder aumentar o valor.

Demorou um pouco para encontrar uma solução satisfatória, espero que isso ajude!

Fabien Snauwaert
fonte
4

Use o excelente plugin jQuery Sparkle. O plug-in oferece a opção de detectar o primeiro e o último clique. Você pode usá-lo para diferenciar entre clique e clique duplo, detectando se outro clique foi seguido pelo primeiro clique.

Confira em http://balupton.com/sandbox/jquery-sparkle/demo/

Hussein
fonte
3

Eu escrevi um plugin jQuery simples que permite que você use um evento personalizado 'singleclick' para diferenciar um único clique de um clique duplo:

https://github.com/omriyariv/jquery-singleclick

$('#someDiv').on('singleclick', function(e) {
    // The event will be fired with a small delay.
    console.log('This is certainly a single-click');
}
Omriyariv
fonte
3

A resposta correta moderna é uma mistura entre a resposta aceita e a solução de @kyw. Você precisa de um tempo limite para evitar o primeiro clique único e a event.detailverificação para evitar o segundo clique.

const button = document.getElementById('button')
let timer
button.addEventListener('click', event => {
  if (event.detail === 1) {
    timer = setTimeout(() => {
      console.log('click')
    }, 200)
  }
})
button.addEventListener('dblclick', event => {
  clearTimeout(timer)
  console.log('dblclick')
})
<button id="button">Click me</button>


fonte
1

Eu gosto de evitar jquery (e outras libs de 90-140k) e, como observado, os navegadores lidam com onclick primeiro, então aqui está o que eu fiz em um site que criei (este exemplo também cobre a obtenção de um local clicado local xy)

clicksNow-0; //global js, owell

function notify2(e, right) {  // called from onclick= and oncontextmenu= (rc)
var x,y,xx,yy;
var ele = document.getElementById('wrap');  
    // offset fixed parent for local win x y
var xxx= ele.offsetLeft;
var yyy= ele.offsetTop;

//NScape
if (document.layers || document.getElementById&&!document.all) {
    xx= e.pageX;
    yy= e.pageY;
} else {
    xx= e.clientX;
    yy= e.clientY;
}
x=xx-xxx;
y=yy-yyy;

clicksNow++;
    // 200 (2/10ths a sec) is about a low as i seem to be able to go
setTimeout( "processClick( " + right + " , " + x + " , " + y + ")", 200);
}

function processClick(right, x, y) {
if (clicksNow==0) return; // already processed as dblclick
if (clicksNow==2) alert('dbl');
clicksNow=0;
    ... handle, etc ...
}

espero que ajude

Dako
fonte
2
O URL do seu jogo não é necessário para entender sua resposta. Estou removendo para que esta postagem não seja vista como uma tentativa de spam.
Andrew Barber
1
o único menos, 200 é a constante mágica e pode variar drasticamente das configurações do mouse do usuário no SO
Danubian Sailor
1

Com base na resposta de Adrien Schuler (muito obrigado !!!), para Datatables.net e para muitos usos, aqui está uma modificação:

Função

/**
 * For handle click and single click in child's objects
 * @param {any} selector Parents selector, like 'tr'
 * @param {any} single_click_callback Callback for single click
 * @param {any} double_click_callback Callback for dblclick
 * @param {any} timeout Timeout, optional, 300 by default
 */
jQuery.fn.single_double_click = function (selector, single_click_callback, double_click_callback, timeout) {
    return this.each(function () {
        let clicks = 0;
        jQuery(this).on('click', selector, function (event) {
            let self = this;
            clicks++;
            if (clicks == 1) {
                setTimeout(function () {
                    if (clicks == 1) {
                        single_click_callback.call(self, event);
                    } else {
                        double_click_callback.call(self, event);
                    }
                    clicks = 0;
                }, timeout || 300);
            }
        });
    });
}

Usar

$("#MyTableId").single_double_click('tr',
            function () {   //  Click
                let row = MyTable.row(this);
                let id = row.id();
                let data = row.data();
                console.log("Click in "+id+" "+data);
            },
            function () {   //  DBLClick
                let row = MyTable.row(this);
                let id = row.id();
                let data = row.data();
                console.log("DBLClick in "+id+" "+data);
            }
        );
Dani
fonte
0

isso funcionou para mim-

var clicked=0;
function chkBtnClcked(evnt) {
    clicked++;
    // wait to see if dblclick
    if (clicked===1) {
        setTimeout(function() {
            clicked=0;
            .
            .
        }, 300); // test for another click within 300ms
    }
    if (clicked===2) {
        stopTimer=setInterval(function() {
            clicked=0;
            .
            .
        }, 30*1000); // refresh every 30 seconds
    }
}

uso-

<div id="cloneimages" style="position: fixed;" onclick="chkBtnClcked(evnt)"  title="Click for next pic; double-click for slide show"></div>
precisa
fonte
0

Apenas postando a resposta HTML nativa apenas no caso de a necessidade ser fácil e HTML.

<p ondblclick="myFunction()" id = 'id'>Double-click me</p>

É claro que isso tem opções nativas do Jquery. ou seja ... $('#id').attr('ondblclick',function(){...})ou, como afirmado anteriormente,$('#id').dblclick(function(){...});

JSG
fonte
0

Eu sei que isso é antigo, mas abaixo está um único exemplo JS de um contador de loop básico com um único temporizador para determinar um clique único vs duplo. Espero que isso ajude alguém.

var count = 0;
var ele = document.getElementById("my_id");
ele.addEventListener('click', handleSingleDoubleClick, false); 

function handleSingleDoubleClick()
{
  if(!count) setTimeout(TimerFcn, 400); // 400 ms click delay
  count += 1;
}
function TimerFcn() 
{
  if(count > 1) console.log('you double clicked!')
  else console.log('you single clicked')
  count = 0;
}
Tim Moses
fonte