Javascript con jQuery: Haga clic y doble clic en el mismo elemento, efecto diferente, uno desactiva el otro


Tengo una situación interesante: tengo una fila de tabla que, actualmente, muestra su contraparte oculta cuando hago clic en el botón "Expandir". La fila original (no oculta) que contiene el botón expandir también tiene algo de contenido en una celda determinada que, cuando se hace clic, se puede editar. Me gustaría deshacerme del botón expandir y habilitar la expansión de la fila a través de doubleclick en cualquier lugar de la fila, incluido el campo que se puede editar al hacer clic en él. Puedes oler el problema aquí ya.

Cuando hago doble clic en una fila, primero se disparan dos eventos de clic, antes de que ocurra el dblclick. Esto significa que si hago doble clic en el campo, se convertirá en uno editable y la fila se expandirá. Me gustaría evitar esto. Quiero que el doubleclick evite el disparo del solo clic, y el solo clic para realizar como de costumbre.

Usando event.stopPropagation () claramente no funcionará ya que son dos eventos diferentes.

¿Alguna idea?

Editar (algunos semi-pseudo código):

Versión original:

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

Versión deseada:

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

Salud

Author: Swader, 2011-03-29

9 answers

La idea general:

  1. En el primer clic, no llame a la función asociada (digamos single_click_function()). Más bien, establezca un temporizador para un cierto período de tiempo(por ejemplo, x). Si no obtenemos otro clic durante ese lapso de tiempo, vaya a la función single_click_function(). Si obtenemos uno, llame a double_click_function()

  2. El temporizador se borrará una vez que se reciba el segundo clic. También se borrará una vez que haya transcurrido x milisegundos.

POR cierto, comprobar Respuesta de Paolo: Necesidad de cancelar los eventos de clic/mouseup cuando se detecta un evento de doble clic y por supuesto todo el hilo! :-)

Mejor respuesta: https://stackoverflow.com/a/7845282/260610

 23
Author: UltraInstinct,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:25:30

Demo de trabajo

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

Obviamente use <td class="dblclickable">

 12
Author: Popnoodles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-20 08:33:27

Reescriba la respuesta de user822711 para su reutilización:

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

Llama a la función.

$('td.dblclickable').bind('click', $.singleDoubleClick(function(e){
  //single click.
}, function(e){
  //double click.
}));
 3
Author: puttyshell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-20 05:01:02

El problema solo ocurre cuando se hace clic en el campo editable, por lo que adjunte un controlador click regular a ese elemento que cancelará la propagación del evento (ver stopPropagation) y establecerá un tiempo de espera (setTimeout(...)) para, digamos, 600ms (el tiempo predeterminado entre dos clics para ser considerado un dbl-click es 500ms [src]). Si, en ese momento el dblclick no ha ocurrido (puede tener un var accesible en ambos controladores de eventos que actúa como una bandera para detectar esto), entonces puede asumir que el usuario quiere expanda la fila en su lugar y continúe con esa acción...

IMO, usted debe re-pensar esto. Por desgracia, un controlador de un solo clic no puede saber si el usuario está a punto de hacer doble clic.

Sugiero hacer ambas acciones con un solo clic, y simplemente no propagar desde el campo editable cuando se hace clic.

 2
Author: James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-29 11:11:44

Escribí un simple plugin de jQuery que te permite usar un evento personalizado 'singleclick' para diferenciar un solo clic de un doble clic. Esto le permite construir una interfaz donde un solo clic hace que la entrada sea editable mientras que un doble clic la expande. Al igual que el sistema de archivos de Windows/OSX renombrar / abrir interfaz de usuario.

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
Author: omriyariv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
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
Author: Philippe Perret,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-05 19:37:36

Añadiendo a la respuesta de @puttyshell, este código tiene espacio para una función visualSingleClick. Esto se ejecuta antes de setTimeout y está destinado a mostrar al usuario alguna acción visual para la acción antes del tiempo de espera, y con eso puedo usar un tiempo de espera mayor. Estoy usando esto en una interfaz de navegación de Google Drive para mostrar al usuario que el archivo o carpeta en la que hizo clic está seleccionado. Google Drive hace algo similar, pero no lo vio código.

/* 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
Author: chronossc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-13 14:26:29

Este ejemplo en javascript vanilla debería funcionar:

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
Author: Ismayil Niftaliyev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-07 10:56:15

Hay una forma más elegante de manejar este problema si estás usando 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
Author: Adam Malchi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-28 09:31:45