Prevenzione dell'evento clic con jQuery drag and drop


85

Ho elementi sulla pagina che possono essere trascinati con jQuery. Questi elementi hanno un evento clic che naviga in un'altra pagina (link ordinari per esempio).

Qual è il modo migliore per impedire che il clic si attivi quando viene rilasciato tale elemento, consentendo allo stesso tempo di non trascinare e rilasciare il clic?

Ho questo problema con gli elementi ordinabili ma penso che sia utile avere una soluzione per il trascinamento generale.

Ho risolto il problema da solo. Dopo di che ho scoperto che esiste la stessa soluzione per Scriptaculous , ma forse qualcuno ha un modo migliore per ottenerlo.


Risposte:


88

Una soluzione che ha funzionato bene per me e che non richiede timeout: (sì, sono un po 'pedante ;-)

Aggiungo una classe marker all'elemento quando inizia il trascinamento, ad esempio "noclick". Quando l'elemento viene rilasciato, viene attivato l'evento clic - più precisamente se il trascinamento termina, in realtà non deve essere rilasciato su un target valido. Nel gestore dei clic, rimuovo la classe marker se presente, altrimenti il ​​clic viene gestito normalmente.

$('your selector').draggable({
    start: function(event, ui) {
        $(this).addClass('noclick');
    }
});

$('your selector').click(function(event) {
    if ($(this).hasClass('noclick')) {
        $(this).removeClass('noclick');
    }
    else {
        // actual click event code
    }
});

1
Utile! Non ha funzionato per me in una volta sola, probabilmente perché stavo scherzando in precedenti tentativi sulla stessa cosa e le classi sono finite su un elemento diverso da quello cliccato .. ma comunque ho usato una variabile globale (che su semplice pagina era ok), e anche questo ha funzionato abbastanza bene.
MSpreij

2
Questo mi ha portato alla mia soluzione, ho appena usato la funzione dati jQuery invece di una classe. Grazie.
William

3
ma il browser non attiva l'evento clic ogni volta che si rilascia un elemento
puchu

6
Per salvare l'impostazione di una classe o dei dati, nel gestore dell'evento click puoi anche utilizzare: if (! $ (This) .is ('. Ui-draggable-dragging')) {... click action}
ChrisV

1
Oops, non vuoi stop: function(event, ui) { $(this).removeClass('noclick'); }nel gestore trascinabile? Altrimenti ogni drag-n-drop da qualche altra parte non ostacolerà il clic successivo sul "selettore".
Bob Stein

41

La soluzione è aggiungere un gestore di clic che impedirà la propagazione del clic all'avvio del trascinamento. Quindi rimuovere quel gestore dopo aver eseguito il rilascio. L'ultima azione dovrebbe essere ritardata un po 'affinché la prevenzione dei clic funzioni.

Soluzione per ordinabile:

...
.sortable({
...
        start: function(event, ui) {
            ui.item.bind("click.prevent",
                function(event) { event.preventDefault(); });
        },
        stop: function(event, ui) {
            setTimeout(function(){ui.item.unbind("click.prevent");}, 300);
        }
...
})

Soluzione per trascinabile:

...
.draggable({
...
        start: function(event, ui) {
            ui.helper.bind("click.prevent",
                function(event) { event.preventDefault(); });
        },
        stop: function(event, ui) {
            setTimeout(function(){ui.helper.unbind("click.prevent");}, 300);
        }
...
})

2
sembra che questo non possa impedire l'evento clic per me, sto usando jquery 1.9.1 e jquery.ui 1.10.3
aaron

1
strano che tu abbia risposto alla tua stessa domanda, e non funziona nemmeno, Sasha lol.

@aaron questa risposta qui sotto funziona: http://stackoverflow.com/a/4572831/119765
lol

Un'altra risposta semplice - basta ° posto jQuery .Click) gestore (dopo .draggable () stackoverflow.com/questions/18032136/...
JxAxMxIxN

1
se si passa l'attributo helper con il valore "clone", si evita di attivare l'evento per l'elemento ordinato trascinato .. {helper: 'clone', start, stop ... etc},
Luchux

12

Ho avuto lo stesso problema e ho provato più approcci e nessuno ha funzionato per me.

Soluzione 1

$('.item').click(function(e)
{            
    if ( $(this).is('.ui-draggable-dragging') ) return false;
});  

non fa niente per me. Si fa clic sull'elemento al termine del trascinamento.

Soluzione 2 (di Tom de Boer)

$('.item').draggable(
{   
    stop: function(event, ui) 
    {
         $( event.originalEvent.target).one('click', function(e){ e.stopImmediatePropagation(); } );
    }
});

Funziona bene ma in un caso non funziona, quando stavo facendo clic a schermo intero:

var body = $('body')[0];     
req = body.requestFullScreen || body.webkitRequestFullScreen || body.mozRequestFullScreen;
req.call(body); 

Soluzione 3 (di Sasha Yanovets)

 $('.item').draggable({
        start: function(event, ui) {
            ui.helper.bind("click.prevent",
                function(event) { event.preventDefault(); });
        },
        stop: function(event, ui) {
            setTimeout(function(){ui.helper.unbind("click.prevent");}, 300);
        }
})

Questo non funziona per me.

Soluzione 4 - l'unica che ha funzionato bene

$('.item').draggable(
{   
});
$('.item').click(function(e)
{  
});

Sì, questo è tutto, l'ordine corretto fa il trucco: prima devi associare trascinabile () quindi fare clic su (). Anche quando ho inserito il codice di commutazione a schermo intero nell'evento click (), non è ancora andato a schermo intero durante il trascinamento. Perfetto per me!


1
Solo uno che ha funzionato per me (in IE11 / Edge) era la soluzione 4. Grazie!
Simon

1
# 4 è la soluzione corretta e più semplice per me, senza usare classi o variabili.
elettrotipo

11

Vorrei aggiungere a questo che sembra che prevenire l'evento clic funzioni solo se l'evento clic è definito DOPO l'evento trascinabile o ordinabile. Se il clic viene aggiunto per primo, viene attivato durante il trascinamento.


9

Non mi piace molto usare timer o prevenire, quindi quello che ho fatto è questo:

var el, dragged

el = $( '#some_element' );

el.on( 'mousedown', onMouseDown );
el.on( 'mouseup', onMouseUp );
el.draggable( { start: onStartDrag } );

onMouseDown = function( ) {
  dragged = false;
}

onMouseUp = function( ) {
  if( !dragged ) {
    console.log('no drag, normal click')
  }
}

onStartDrag = function( ) {
  dragged = true;
}

Roccia solida..


Questa è stata la soluzione migliore per me. Ho avuto problemi a far funzionare gli eventi clic su un'interfaccia utente jquery ordinabile con un browser Web mobile utilizzando jquery.ui.touch.punch.js, ma questo ha risolto il problema in modo abbastanza elegante.
Godsmith

3

versione di lex82 ma per .sortable ()

 start: function(event, ui){
 ui.item.find('.ui-widget-header').addClass('noclick');
 },

e potresti aver bisogno solo di:

 start: function(event, ui){
 ui.item.addClass('noclick');
 },

ed ecco cosa sto usando per l'interruttore:

$("#datasign-widgets .ui-widget-header").click(function(){
if ($(this).hasClass('noclick')) {
$(this).removeClass('noclick');

}
else {
$(this).next().slideToggle();
$(this).find('.ui-icon').toggleClass("ui-icon-minusthick").toggleClass("ui-icon-plusthick");
}
});

2
Sembra che l'interfaccia utente di jquery aggiunga la classe "ui-sortable-helper" all'elemento che viene ordinato. Quindi, puoi omettere la classe 'noclick' e fare semplicemente if ($ (this) .hasClass ('ui-sortable-helper')). Più conciso in questo modo
Matt De Leon

3

Una possibile alternativa alla risposta di Sasha senza impedire il default:

var tmp_handler;
.sortable({
        start : function(event,ui){
            tmp_handler = ui.item.data("events").click[0].handler;
            ui.item.off();
        },
        stop : function(event,ui){
            setTimeout(function(){ui.item.on("click", tmp_handler)}, 300);
        },

3

In jQuery UI, agli elementi trascinati viene assegnata la classe "ui-trascinabile-trascinamento".
Possiamo quindi utilizzare questa classe per determinare se fare clic o meno, ritardare semplicemente l'evento.
Non è necessario utilizzare le funzioni di callback "start" o "stop", basta fare:

$('#foo').on('mouseup', function () {
    if (! $(this).hasClass('ui-draggable-dragging')) {
        // your click function
    }
});

Questo viene attivato da "mouseup", piuttosto che "mousedown" o "clic" - quindi c'è un leggero ritardo, potrebbe non essere perfetto - ma è più facile di altre soluzioni suggerite qui.


1

Dopo aver letto questo e alcuni thread, questa è stata la soluzione con cui sono andato.

var dragging = false;
$("#sortable").mouseover(function() {
    $(this).parent().sortable({
        start: function(event, ui) {
            dragging = true;
        },
        stop: function(event, ui) {
            // Update Code here
        }
    })
});
$("#sortable").click(function(mouseEvent){
    if (!dragging) {
        alert($(this).attr("id"));
    } else {
        dragging = false;
    }
});

1

Nel mio caso ha funzionato così:

$('#draggable').draggable({
  start: function(event, ui) {
    $(event.toElement).one('click', function(e) { e.stopPropagation(); });
  }
});

0

Hai provato a disabilitare il collegamento usando event.preventDefault (); nell'evento di inizio e riattivarlo nell'evento di interruzione del trascinamento o evento di rilascio utilizzando unbind?


0

Solo una piccola ruga da aggiungere alle risposte fornite sopra. Ho dovuto creare un div che contiene un elemento SalesForce trascinabile, ma l'elemento SalesForce ha un'azione onclick definita nell'html tramite alcuni gobbledigook di VisualForce.

Ovviamente questo viola la regola "definire l'azione di clic dopo l'azione di trascinamento", quindi come soluzione alternativa ho ridefinito l'azione dell'elemento SalesForce da attivare "onDblClick" e ho utilizzato questo codice per il div contenitore:

$(this).draggable({
        zIndex: 999,
        revert: true,
        revertDuration: 0,
        start: function(event, ui) {
                   $(this).addClass('noclick');
                }
});

$(this).click(function(){
    if( $(this).hasClass('noclick'))
    {
        $(this).removeClass('noclick');
    }
    else
    {
        $(this).children(":first").trigger('dblclick');
    }
});

L'evento clic del genitore nasconde essenzialmente la necessità di fare doppio clic sull'elemento figlio, lasciando intatta l'esperienza dell'utente.


0

Ho provato in questo modo:

var dragging = true; 

$(this).click(function(){
  if(!dragging){
    do str...
  }
});

$(this).draggable({
  start: function(event, ui) {
      dragging = true;
  },

  stop: function(event, ui) {
      setTimeout(function(){dragging = false;}, 300);
  }

});

0

per me ha aiutato a passare l'helper nell'oggetto opzioni come:

.sortable({
   helper : 'clone', 
   start:function(), 
   stop:function(),
   .....
});

Sembra che la clonazione dell'elemento dom trascinato abbia impedito il bubbling dell'evento. Non ho potuto evitarlo con qualsiasi eventPropagation, bubbling, ecc. Questa era l'unica soluzione funzionante per me.


0

Gli eventi onmousedown e onmouseup hanno funzionato in uno dei miei progetti più piccoli.

var mousePos = [0,0];
function startClick()
{
    mousePos = [event.clientX,event.clientY];
}
        
function endClick()
{
    if ( event.clientX != mousePos[0] && event.clientY != mousePos[1] )
    {
        alert( "DRAG CLICK" );
    }
    else
    {
        alert( "CLICK" );
    }
}
<img src=".." onmousedown="startClick();" onmouseup="endClick();" />

Si, lo so. Non è il modo più pulito, ma hai l'idea.


0

la soluzione più semplice e robusta? basta creare un elemento trasparente sul tuo trascinabile.

.click-passthrough {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background: transparent;
}

element.draggable({        
        start: function () {

        },
        drag: function(event, ui) {
            // important! if create the 'cover' in start, then you will not see click events at all
                  if (!element.find('.click-passthrough').length) {
                      element.append("<div class='click-passthrough'></div>");
                  }
        },
        stop: function() {
          // remove the cover
          element.find('.click-passthrough').remove();
        }
    });
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.