Empêcher l'événement de clic avec le glisser-déposer jQuery


85

J'ai des éléments sur la page qui peuvent être déplacés avec jQuery. Ces éléments ont-ils un événement de clic qui navigue vers une autre page (liens ordinaires par exemple).

Quelle est la meilleure façon d'empêcher le clic de se déclencher lors de la chute d'un tel élément tout en permettant de cliquer sur l'état du glisser-déposer?

J'ai ce problème avec les éléments triables mais je pense qu'il est bon d'avoir une solution pour le glisser-déposer général.

J'ai résolu le problème moi-même. Après cela, j'ai trouvé que la même solution existe pour Scriptaculous , mais peut-être que quelqu'un a un meilleur moyen d'y parvenir.


Réponses:


88

Une solution qui a bien fonctionné pour moi et qui ne nécessite pas de timeout: (oui je suis un peu pédant ;-)

J'ajoute une classe de marqueur à l'élément au début du glissement, par exemple «noclick». Lorsque l'élément est déposé, l'événement de clic est déclenché - plus précisément si le glissement se termine, il n'est en fait pas nécessaire de le déposer sur une cible valide. Dans le gestionnaire de clics, je supprime la classe de marqueur si elle est présente, sinon le clic est géré normalement.

$('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! Cela ne fonctionnait pas pour moi en une seule fois, probablement parce que j'avais raté lors de précédentes tentatives de la même chose et que les classes se sont retrouvées sur un élément différent de celui sur lequel j'ai cliqué ... mais de toute façon, j'ai utilisé une variable globale (qui sur ce extrêmement page simple était ok), et cela fonctionnait très bien aussi.
MSpreij

2
Cela m'a conduit à ma solution, je viens d'utiliser la fonction de données jQuery au lieu d'une classe. Merci.
William

3
mais le navigateur ne déclenchera pas l'événement de clic chaque fois que vous déposez un élément
puchu

6
Pour enregistrer le réglage d'une classe ou de données, dans le gestionnaire d'événements click, vous pouvez également utiliser: if (! $ (This) .is ('. Ui-draggable-dragging')) {... click action}
ChrisV

1
Oups, vous ne voulez pas stop: function(event, ui) { $(this).removeClass('noclick'); }dans le gestionnaire déplaçable? Sinon, chaque glisser-déposer ailleurs ne contrariera pas le prochain clic sur «votre sélecteur».
Bob Stein

41

La solution consiste à ajouter un gestionnaire de clic qui empêchera le clic de se propager au début du glissement. Et puis supprimez ce gestionnaire après la suppression. La dernière action doit être un peu retardée pour que la prévention des clics fonctionne.

Solution pour triable:

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

Solution pour glisser:

...
.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
semble que cela ne puisse pas empêcher l'événement de clic pour moi, im utilisant jquery 1.9.1 et jquery.ui 1.10.3
aaron

1
bizarre que tu aies répondu à ta propre question, et ça ne marche même pas, Sasha lol.

@aaron cette réponse ci-dessous fonctionne: http://stackoverflow.com/a/4572831/119765
lol

Une autre réponse simple - placez simplement le gestionnaire jQuery .click () après .draggable () stackoverflow.com/questions/18032136
...

1
si vous passez l'attribut helper avec la valeur 'clone', cela évite de déclencher l'événement sur l'élément trié par glisser. {helper: 'clone', start, stop ... etc},
Luchux

12

J'ai eu le même problème et j'ai essayé plusieurs approches et aucune n'a fonctionné pour moi.

Solution 1

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

ne fait rien pour moi. L'élément est cliqué une fois le glissement terminé.

Solution 2 (par Tom de Boer)

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

Cela fonctionne très bien mais échoue dans un cas - lorsque j'allais en plein écran en cliquant sur:

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

Solution 3 (par 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);
        }
})

Cela ne fonctionne pas pour moi.

Solution 4 - la seule qui a bien fonctionné

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

Oui, c'est tout - le bon ordre fait l'affaire - vous devez d'abord lier l'événement draggable () puis click (). Même lorsque j'ai mis du code de basculement en plein écran dans l'événement click (), il ne passait toujours pas en plein écran lors du glissement. Parfait pour moi!


1
Un seul qui a fonctionné pour moi (dans IE11 / Edge) était la solution 4. Merci!
Simon

1
# 4 est la solution correcte et la plus simple pour moi, sans utiliser de classes ou de variables.
electrotype

11

Je voudrais ajouter à cela qu'il semble empêcher l'événement de clic ne fonctionne que si l'événement de clic est défini APRÈS l'événement déplaçable ou triable. Si le clic est ajouté en premier, il est activé par glisser.


9

Je n'aime pas vraiment utiliser les minuteries ou la prévention, alors ce que j'ai fait est ceci:

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

Solide comme le roc..


C'était la meilleure solution pour moi. J'ai eu du mal à faire fonctionner les événements de clic sur une interface utilisateur jquery triable dans un navigateur Web mobile à l'aide de jquery.ui.touch.punch.js, mais cela l'a résolu de manière assez élégante.
Godsmith

3

version de lex82 mais pour .sortable ()

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

et vous n'aurez peut-être besoin que de:

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

et voici ce que j'utilise pour la bascule:

$("#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
Il semble que jquery UI ajoute la classe «ui-sortable-helper» à l'élément en cours de tri. Ainsi, vous pouvez laisser de côté la classe 'noclick' et faire simplement if ($ (this) .hasClass ('ui-sortable-helper')). Plus laconique de cette façon
Matt De Leon

3

Une alternative possible à la réponse de Sasha sans empêcher le défaut:

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

Dans jQuery UI, les éléments glissés reçoivent la classe "ui-draggable-dragging".
On peut donc utiliser cette classe pour déterminer s'il faut cliquer ou non, simplement retarder l'événement.
Vous n'avez pas besoin d'utiliser les fonctions de rappel "start" ou "stop", faites simplement:

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

Ceci est déclenché par "mouseup", plutôt que "mousedown" ou "click" - il y a donc un léger retard, peut-être pas parfait - mais c'est plus facile que d'autres solutions suggérées ici.


1

Après avoir lu ceci et quelques fils, c'était la solution avec laquelle je suis allé.

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

Dans mon cas, cela a fonctionné comme ceci:

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

0

Avez-vous essayé de désactiver le lien en utilisant event.preventDefault (); dans l'événement de démarrage et le réactiver dans l'événement de glisser arrêté ou de déposer à l'aide de la suppression?


0

Juste une petite ride à ajouter aux réponses données ci-dessus. J'ai dû créer un div contenant un élément SalesForce pouvant être déplacé, mais l'élément SalesForce a une action onclick définie dans le html via un gobbledigook VisualForce.

Évidemment, cela enfreint la règle «définir l'action de clic après l'action de glisser», donc comme solution de contournement, j'ai redéfini l'action de l'élément SalesForce pour qu'elle soit déclenchée «onDblClick», et utilisé ce code pour le conteneur div:

$(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'événement de clic du parent masque essentiellement la nécessité de double-cliquer sur l'élément enfant, laissant ainsi l'expérience utilisateur intacte.


0

J'ai essayé comme ça:

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

pour moi, j'ai aidé à passer l'assistant dans l'objet d'options comme:

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

Semble l'élément dom de clonage qui est glissé a empêché le bouillonnement de l'événement. Je ne pouvais pas l'éviter avec n'importe quel eventPropagation, bouillonnement, etc. C'était la seule solution qui fonctionnait pour moi.


0

Les événements onmousedown et onmouseup ont fonctionné dans l'un de mes petits projets.

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();" />

Oui je sais. Ce n'est pas la manière la plus propre, mais vous voyez l'idée.


0

la solution la plus simple et la plus robuste? créez simplement un élément transparent sur votre draggable.

.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();
        }
    });
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.