web-dev-qa-db-fra.com

Prévention des événements liés aux clics avec un glisser-déposer jQuery

J'ai un élément sur la page qui sont draggabe avec jQuery. Ces éléments ont un événement de clic qui permet de naviguer vers une autre page (liens ordinaires par exemple).

Quel est le meilleur moyen d’empêcher le clic de se déclencher en déposant un tel élément, tout en permettant de cliquer dessus sans glisser-déposer?

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

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

78
Alexander Yanovets

La solution consiste à ajouter un gestionnaire de clics qui empêchera le clic de se propager au début du glissement. Et ensuite, 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 draggable:

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

Une solution qui a bien fonctionné pour moi et qui n'exige pas de délai d'attente: (oui je suis un peu pédant ;-)

J'ajoute une classe de marqueur à l'élément lorsque le glissement commence, par exemple. 'noclick'. Lorsque l'élément est supprimé, l'événement click est déclenché - plus précisément si le glissement se termine, il n'est en réalité 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 traité 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
    }
});
85
lex82

J'aimerais ajouter à cela qu'il semble empêcher que l'événement click ne fonctionne que si l'événement click est défini APRÈS l'événement déplaçable ou pouvant être trié. Si le clic est ajouté en premier, il est activé par glisser.

11
kasakka

J'ai eu le même problème et 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 en cours de clic une fois le déplacement terminé.

Solution 2 (de 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, quand j'allais en plein écran onclick:

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- le seul qui a bien fonctionné

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

Oui, c'est ça - le bon ordre fait l'affaire - vous devez d'abord lier l'événement draggable (), puis cliquer sur (). Même lorsque je mets le code de basculement plein écran dans l'événement click (), il ne passait toujours pas en mode plein écran lors du déplacement. Parfait pour moi!

11
Tom

J'ai constaté qu'en utilisant la fonction de clic standard jQuery:

$("#element").click(function(mouseEvent){ // click event });

fonctionne très bien dans l'interface utilisateur de jQuery. L'événement click ne sera pas exécuté lors du glisser-déposer. :)

9
Norm

Je n'aime pas trop utiliser les minuteries ou prévenir, alors voici ce que j'ai fait:

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

8
Tim Baas

Dans jQuery UI, la classe "ui-draggable-dragging" est attribuée aux éléments glissés.
Nous pouvons donc utiliser cette classe pour déterminer s’il faut cliquer ou non, il suffit de 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 par "mousedown" ou "click" - il y a donc un léger retard, ce n'est peut-être pas parfait - mais c'est plus facile que d'autres solutions suggérées ici.

3
lukesrw

Une alternative possible à la réponse de Sasha sans prévenir les défauts:

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

Version de Lex82 mais pour .sortable ()

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

et vous n'aurez 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");
}
});
3
orolo

Après avoir lu ceci et quelques fils, c’était la solution à laquelle j’allais.

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
jwdreger

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
sun

Dans mon cas, cela a fonctionné comme ceci:

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

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êter ou l'événement de dépôt à l'aide de unbind?

0
alienonland

Juste une petite ride à ajouter aux réponses données ci-dessus. J'ai dû créer une division contenant un élément SalesForce déplaçable, mais l'élément SalesForce comporte une action onclick définie dans le code HTML via un gobbledigook VisualForce.

Évidemment, cela viole la règle "définir l'action du clic après l'action du glisser". C'est pourquoi, comme solution de contournement, j'ai redéfini l'action de l'élément SalesForce pour qu'elle soit déclenchée "onDblClick" et ai 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 le besoin de double-cliquer sur l'élément enfant, laissant ainsi l'expérience utilisateur intacte.

0
geekbrit