web-dev-qa-db-fra.com

Javascript avec jQuery: Cliquez et double-cliquez sur le même élément, effet différent, l'un désactive l'autre

J'ai une situation intéressante - j'ai une ligne de table qui, actuellement, montre sa contrepartie cachée lorsque je clique sur le bouton "Développer". La ligne d'origine (non masquée) contenant le bouton Développer contient également du contenu dans une cellule donnée qui, une fois cliqué, devient modifiable. Je souhaite supprimer le bouton Développer et permettre le développement de la ligne via un double-clic n'importe où dans la ligne elle-même, y compris le champ qui devient modifiable lorsque vous cliquez dessus. Vous pouvez déjà sentir le problème ici.

Lorsque je double-clique sur une ligne, deux événements de clic sont déclenchés avant le dblclick. Cela signifie que si je double-clique sur le champ, il se transforme en modifiable et la ligne s’agrandit. Je voudrais empêcher cela. Je veux que le double clic empêche le déclenchement du clic simple et que le clic simple fonctionne comme d'habitude.

Utiliser event.stopPropagation () ne fonctionnera évidemment pas car il s'agit de deux événements différents.

Des idées?

Edit (un peu de pseudo-code):

Version originale:

<table>
    <tbody>
        <tr>
            <td><a href="javascript:$('#row_to_expand').toggle();" title="Expand the hidden row">Expand Row</a></td>
            <td>Some kind of random data</td>
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[0]->value("First editable value") ?></td>
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[1]->value("Second editable value") ?></td>
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[2]->value("Third editable value") ?></td>
            <!-- ... -->
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[n]->value("Nth editable value") ?></td>
        </tr>
        <tr style="display: none" id="row_to_expand">
            <td colspan="n">Some hidden data</td>
        </tr>
    </tbody>
</table>

Version souhaitée:

<table>
    <tbody>
        <tr ondblclick="$('#row_to_expand').toggle()">
            <td>Some kind of random data</td>
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[0]->value("First editable value") ?></td>
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[1]->value("Second editable value") ?></td>
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[2]->value("Third editable value") ?></td>
            <!-- ... -->
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[n]->value("Nth editable value") ?></td>
        </tr>
        <tr style="display: none" id="row_to_expand">
            <td colspan="n">Some hidden data</td>
        </tr>
    </tbody>
</table>

À votre santé

39
Swader

L'idée générale:

  1. Au premier clic, n'appelez pas la fonction associée (disons single_click_function ()). Réglez plutôt une minuterie pour une certaine période (disons x). Si nous n'obtenons pas un autre clic au cours de cette période, optez pour single_click_function (). Si nous en avons un, appelez double_click_function ()

  2. La minuterie sera effacée une fois le deuxième clic reçu. Il sera également effacé une fois x millisecondes écoulées.

BTW, vérifiez la réponse de Paolo: Besoin d'annuler les événements click/mouseup quand un événement double-clic est détecté :-)

Meilleure réponse: https://stackoverflow.com/a/7845282/260610

23
UltraInstinct

Démo de travail

$('#alreadyclicked').val('no click');
$('td.dblclickable').on('click',function(){
    var $button=$(this);
    if ($button.data('alreadyclicked')){
        $button.data('alreadyclicked', false); // reset
        $('#alreadyclicked').val('no click');

        if ($button.data('alreadyclickedTimeout')){
            clearTimeout($button.data('alreadyclickedTimeout')); // prevent this from happening
        }
        // do what needs to happen on double click. 
        $('#action').val('Was double clicked');
    }else{
        $button.data('alreadyclicked', true);
        $('#alreadyclicked').val('clicked');
        var alreadyclickedTimeout=setTimeout(function(){
            $button.data('alreadyclicked', false); // reset when it happens
            $('#alreadyclicked').val('no click');
            $('#action').val('Was single clicked');
            // do what needs to happen on single click. 
            // use $button instead of $(this) because $(this) is 
            // no longer the element
        },300); // <-- dblclick tolerance here
        $button.data('alreadyclickedTimeout', alreadyclickedTimeout); // store this id to clear if necessary
    }
    return false;
});

utiliser évidemment <td class="dblclickable">

12
Popnoodles

Réécrivez la réponse de user822711 pour la réutiliser:

  $.singleDoubleClick = function(singleClk, doubleClk) {
    return (function() {
      var alreadyclicked = false;
      var alreadyclickedTimeout;

      return function(e) {
        if (alreadyclicked) {
          // double
          alreadyclicked = false;
          alreadyclickedTimeout && clearTimeout(alreadyclickedTimeout);
          doubleClk && doubleClk(e);
        } else {
          // single
          alreadyclicked = true;
          alreadyclickedTimeout = setTimeout(function() {
            alreadyclicked = false;
            singleClk && singleClk(e);
          }, 300);
        }
      }
    })();
  }

Appelle la fonction.

$('td.dblclickable').bind('click', $.singleDoubleClick(function(e){
  //single click.
}, function(e){
  //double click.
}));
3
puttyshell

J'ai écrit un simple plugin jQuery qui vous permet d'utiliser un événement personnalisé 'clic simple' pour différencier un simple clic d'un double-clic. Cela vous permet de créer une interface dans laquelle un simple clic rend l’entrée modifiable tandis qu’un double-clic la développe. Tout comme le système de fichiers Windows/OSX renommer/ouvrir l'interface utilisateur. 

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

$('#someInput').on('singleclick', function(e) {
    // The event will be fired with a small delay but will not fire upon a double-click.
    makeEditable(e.target);
}

$('#container').on('dblclick', function(e) {
    // Expand the row...
}
2
omriyariv

Le problème ne se produit que lorsque l'utilisateur clique sur le champ éditable. Joignez donc un gestionnaire click standard à cet élément qui annulera la propagation de l'événement ( voir stopPropagation ) et définira un délai d'expiration (setTimeout(...)) pour, par exemple, 600 ms (le temps par défaut entre deux clics pour être considéré comme un clic-disque est de 500 ms [src]). Si, à ce moment-là, la dblclick ne s'est pas produite (vous pouvez avoir une variable accessible dans les deux gestionnaires d'événements qui sert d'indicateur pour le détecter), vous pouvez donc supposer que l'utilisateur souhaite plutôt développer la ligne et poursuivre l'action ...

OMI, vous devriez repenser cela. Hélas, un gestionnaire de simple clic ne peut pas savoir si l'utilisateur est sur le point de double-cliquer.

Je suggère de faire les deux actions en un seul clic, et simplement de ne pas se propager à partir du champ éditable quand on clique dessus.

2
James
window.UI = {
  MIN_LAPS_FOR_DBL_CLICK: 200, // msecs

  evt_down:null,
  timer_down:null,
  mousedown:function(evt){
    if( this.evt_down && evt.timeStamp < this.evt_down.timeStamp + this.MIN_LAPS_FOR_DBL_CLICK ){
      clearTimeout(this.timer_down);
      this.double_click(evt); // => double click
    } else {
      this.evt_down   = evt;
      this.timer_down = setTimeout("UI.do_on_mousedown()", this.MIN_LAPS_FOR_DBL_CLICK);
    }
  },
  evt_up:null,
  timer_up:null,
  mouseup:function(evt){
    if( this.evt_up && evt.timeStamp < this.evt_up.timeStamp + this.MIN_LAPS_FOR_DBL_CLICK ){
      clearTimeout(this.timer_up);
    } else {
      this.evt_up   = evt;
      this.timer_up = setTimeout("UI.do_on_mouseup()", this.MIN_LAPS_FOR_DBL_CLICK);
    }
  },
  do_on_mousedown:function(){
    var evt = this.evt_down;
    // Treatment MOUSE-DOWN here
  },
  do_on_mouseup:function(){
    var evt = this.evt_up;
    // Treatment MOUSE-UP here
  },
  double_click:function(evt){
    // Treatment DBL-CLICK here
  }

}

// Then the observers

$('div#myfoo').bind('mousedown',  $.proxy(UI.mousedown, UI));
$('div#myfoo').bind('mouseup',    $.proxy(UI.mouseup, UI));
1
Philippe Perret

Cet exemple dans Vanilla JavaScript devrait fonctionner:

var timer = 0
var delay = 200
var prevent = false

node.onclick = (evnt) => {
    timer = setTimeout(() => {
         if(!prevent){
             //Your code
         }
         prevent = false
    }, delay)
}
node.ondblclick = (evnt) => {
    clearTimeout(timer)
    prevent=true
    //Your code
}
1

Ajoutant à répondre de @puttyshell, ce code a de l'espace pour une fonction visualSingleClick. Cette exécution avant setTimeout a pour but de montrer à l’utilisateur une action visuelle pour l’action avant le délai d’attente, ce qui me permet d’utiliser un délai d’attente plus long. J'utilise ceci sur une interface de navigation de Google Drive pour indiquer à l'utilisateur que le fichier ou le dossier sur lequel il a cliqué est sélectionné. Google Drive lui-même fait quelque chose de similaire, mais ne l'a pas vu code.

/* plugin to enable single and double click on same object */
$.singleDoubleClick = function(visualSingleClk, singleClk, doubleClk) {
  return (function() {
    var alreadyclicked = false;
    var alreadyclickedTimeout;

    return function(e) {
      if (alreadyclicked) {
        // double
        alreadyclicked = false;
        alreadyclickedTimeout && clearTimeout(alreadyclickedTimeout);
        doubleClk && doubleClk(e);
      } else {
        // single
        alreadyclicked = true;
        visualSingleClk && visualSingleClk(e);
        alreadyclickedTimeout = setTimeout(function() {
          alreadyclicked = false;
          singleClk && singleClk(e);
        }, 500);
      }
    }
  })();
}
0
chronossc