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 rangée de tableau qui, actuellement, montre son pendant caché quand je clique sur le bouton" étendre". La ligne originale (non autorisée) qui contient le bouton expand contient aussi du contenu dans une certaine cellule qui, lorsqu'on clique, devient modifiable. Je voudrais être débarrassé du bouton expand, et permettre l'expansion de la ligne via doubleclick n'importe où dans la ligne elle-même, y compris le champ qui devient modifiable lorsque vous cliquez dessus. Tu peux sentir le problème ici déjà.

quand je double clique sur une ligne, deux évènements de clic sont lancés en premier, avant que le dblclick ne se produise. Cela signifie que si je double-clique sur le champ, il se transformera en un modifiable, et la rangée se développera. Je voudrais éviter cela. Je veux que le doubleclick empêche la mise à feu du simple clic, et que le simple clic fonctionne comme d'habitude.

utilisant event.stopPropagation () ne fonctionnera clairement pas puisqu'il s'agit de deux événements différents.

des idées?

Modifier (quelques demi-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>

Cheers

39
demandé sur Swader 2011-03-29 14:46:16

9 réponses

L'idée générale:

  1. au premier clic, n'appelez pas la fonction associée (dites single_click_function()). Il faut plutôt fixer un minuteur pour une certaine période de temps(disons x). Si nous n'obtenons pas un autre clic pendant cette période, optez pour la fonction single_click_function(). Si nous en obtenons un, appelez double_click_function ()

  2. Timer sera effacé une fois que le deuxième clic est reçu. Il sera également nettoyé une fois que x millisecondes sont périmées.

BTW, vérifiez Paolo de répondre: Besoin de l'annuler, cliquez sur/mouseup événements lorsque vous double-cliquez sur l'événement détecté et bien sûr tout le fil! :- )

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

23
répondu UltraInstinct 2017-05-23 12:25:30

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

évidemment utiliser <td class="dblclickable">

12
répondu Popnoodles 2014-08-20 08:33:27

Réécrire user822711 réponse à la réutilisation:

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

appelez la fonction.

$('td.dblclickable').bind('click', $.singleDoubleClick(function(e){
  //single click.
}, function(e){
  //double click.
}));
3
répondu puttyshell 2018-09-20 05:01:02

le problème ne se produit que lorsque le champ modifiable est cliqué, alors attachez un gestionnaire régulier click à cet élément qui annulera la propagation de l'événement ( voir stopPropagation ) et définira un délai ( setTimeout(...) ) pour, disons, 600ms ( le délai par défaut entre deux clics pour être considéré comme un DBL-click est de 500ms [src] ). Si, à ce moment-là, le dblclick ne s'est pas produit (vous pouvez avoir un var accessible dans les deux cas les gestionnaires qui agit comme un drapeau pour détecter cela), alors vous pouvez supposer que l'utilisateur veut étendre la ligne la place et de poursuivre cette action...

IMO, tu devrais repenser à ça. Hélas, un gestionnaire de clic simple 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 de ne pas se propager à partir du champ modifiable quand il est cliqué.

2
répondu James 2011-03-29 11:11:44

j'ai écrit un simple plugin jQuery qui vous permet d'utiliser un événement 'singleclick' personnalisé pour différencier un simple-clic d'un double-clic. Cela vous permet de construire une interface où un simple clic, rend l'entrée modifiable en un double-cliquez sur l'étend. Tout comme Windows/OSX rename / open UI.

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
répondu omriyariv 2015-09-27 14:02:10
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
répondu Philippe Perret 2013-10-05 19:37:36

ajoutant pour répondre de @puttyshell, ce code a de l'espace pour une fonction visualSingleClick . Cette course avant setTimeout et est destiné à montrer à l'utilisateur une certaine action visuelle pour l'action avant l'expiration du temps, et avec cela je peux utiliser une plus grande expiration du temps. Je l'utilise sur une interface de navigation de Google Drive pour montrer à l'utilisateur que le fichier ou le dossier qu'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
répondu chronossc 2015-09-13 14:26:29

cet exemple en javascript vanille 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
}
0
répondu Ismayil Niftaliyev 2018-06-07 10:56:15

il y a une façon plus élégante de traiter cette question si vous utilisez backbonejs:


    initialize: function(){
        this.addListener_openWidget();
    }

    addListener_openWidget: function(){
        this.listenToOnce( this, 'openWidgetView', function(e){this.openWidget(e)} );
    },

    events: {
        'click #openWidgetLink' : function(e){this.trigger('openWidgetView',e)},
        'click #closeWidgetLink' : 'closeWidget'
    },

    openWidget: function( e ){
       //Do your stuff
    },

    closeWidget: function(){
       this.addListener_openWidget();
    }

-2
répondu Adam Malchi 2015-01-28 09:31:45