web-dev-qa-db-fra.com

Annuler l'événement click dans le gestionnaire d'événements mouseup

En écrivant du code glisser-déposer, j'aimerais annuler les événements de clic dans mon gestionnaire de souris. Je pensais qu'empêcher le défaut devrait faire l'affaire, mais l'événement click est toujours déclenché.

Y a-t-il un moyen de faire cela?


Cela ne marche pas:

<div id="test">test</div>
<script>
$("#test").mouseup (function (e) {
  var a = 1;
  e.preventDefault();
});
$("#test").click (function (e) {
  var a = 2;
});
23
Yaron

J'ai eu le même problème et je n'ai pas trouvé de solution non plus. Mais je suis venu avec un bidouillage qui semble fonctionner. 

Comme le gestionnaire onMouseUp ne semble pas pouvoir annuler le clic sur un lien avec preventDefault ou stopEvent ou quoi que ce soit, nous devons faire en sorte que le lien s'annule lui-même. Cela peut être fait en écrivant un attribut onclick qui retourne false à l'a-tag lorsque le glissement commence, et en le supprimant à la fin du glissement. 

Et comme les gestionnaires onDragEnd ou onMouseUp sont exécutés avant que le clic ne soit interprété par le navigateur, nous devons vérifier où se termine le glisser-déposer et quel lien est cliqué, etc. S'il se termine en dehors du lien glissé (nous avons fait glisser le lien pour que le curseur ne soit plus sur le lien), nous supprimons le gestionnaire onclick dans onDragEnd; mais s'il se termine là où le curseur est sur le lien glissé (un clic serait déclenché), nous laissons le gestionnaire onclick se supprimer. Assez compliqué, non?

PAS CODE COMPLET, mais juste pour vous montrer l'idée:

// event handler that runs when the drag is initiated
function onDragStart (args) {
  // get the dragged element
  // do some checking if it's a link etc
  // store it in global var or smth

  // write the onclick handler to link element
  linkElement.writeAttribute('onclick', 'removeClickHandler(this); return false;');
}

// run from the onclick handler in the a-tag when the onMouseUp occurs on the link
function removeClickHandler (element) {
  // remove click handler from self
  element.writeAttribute('onclick', null);
}

// event handler that runs when the drag ends
function onDragEnds (args) {
  // get the target element

  // check that it's not the a-tag which we're dragging,
  // since we want the onclick handler to take care of that case
  if (targetElement !== linkElement) {
    // remove the onclick handler
    linkElement.writeAttribute('onclick', null);
  }
}

J'espère que cela vous donne une idée de la façon dont cela peut être accompli. Comme je l'ai dit, ce n'est pas une solution complète, juste pour expliquer le concept. 

4
Fredrik Boström

Utiliser la phase de capture d'événement

Placez un élément autour de l'élément pour lequel vous souhaitez annuler l'événement click et ajoutez-lui un gestionnaire d'événements de capture.

var btnElm = document.querySelector('button');

btnElm.addEventListener('mouseup', function(e){
    console.log('mouseup');
    
    window.addEventListener(
        'click',
        captureClick,
        true // <-- This registeres this listener for the capture
             //     phase instead of the bubbling phase!
    ); 
});

btnElm.addEventListener('click', function(e){
    console.log('click');
});

function captureClick(e) {
    e.stopPropagation(); // Stop the click from being propagated.
    console.log('click captured');
    window.removeEventListener('click', captureClick, true); // cleanup
}
<button>Test capture click event</button>

JSFiddle Demo

Ce qui se produit:

Avant que l'événement click sur la button ne soit déclenché, l'événement click sur la div environnante est déclenché car il s'est enregistré pour la phase de capture au lieu de la phase de propagation.

Le gestionnaire captureClick arrête ensuite la propagation de son événement click et empêche le gestionnaire click du bouton d'être appelé. Exactement ce que tu voulais. Il se supprime ensuite pour le nettoyage.

Capture vs bouillonnement:

La phase de capture est appelée à partir de la racine DOM jusqu'aux feuilles, tandis que la phase de formation de bulles commence à la racine (voir: explication merveilleuse de l'ordre des événements ).

jQuery ajoute toujours des événements à la phase de bullage. C'est pourquoi nous devons utiliser ici du JS pur pour ajouter notre événement de capture spécifiquement à la phase de capture.

N'oubliez pas que IE a introduit le modèle de capture d'événement du W3C avec IE9 afin que cela ne fonctionne pas avec IE <9.


Avec l'API d'événements actuelle, vous ne pouvez pas ajouter de nouveau gestionnaire d'événements à un élément DOM avant un autre déjà ajouté. Il n'y a pas de paramètre de priorité et il n'y a pas de solution multi-navigateur sûre pour modifier la liste des écouteurs d'événement .

16
flu

Il y a une solution!

Cette approche fonctionne très bien pour moi (au moins en chrome):

sur mousedown j'ajoute une classe à l'élément en cours de déplacement et sur mouseup je supprime la classe.

Tout ce que cette classe fait, c'est pointer-events:none

D'une certaine manière, cela fonctionne et l'événement click n'est pas déclenché.

7
FDIM

Le problème est qu'il y a un élément. Il doit réagir aux clics. Il faut aussi le faire glisser. Cependant, lorsqu'il est déplacé, il ne doit pas déclencher de clic lorsqu'il est supprimé.

Un peu tard, mais peut-être que ça va aider quelqu'un d'autre. Créez une variable globale appelée "noclick" ou quelque chose de différent et définissez-la sur false. Lorsque vous faites glisser l'élément, définissez noclick sur true. Dans votre gestionnaire de clics, si noclick est défini sur true, définissez-le sur false, puis sur preventDefault, renvoyez false, stopPropagation, etc. Cela ne fonctionnera pas sous Chrome, car Chrome a déjà le comportement souhaité. Il suffit de faire en sorte que la fonction de déplacement ne définisse noclick que si le navigateur n'est pas Chrome.

Votre gestionnaire de clics sera toujours renvoyé, mais au moins, il a le moyen de savoir qu'il vient de revenir de glisser et se comporte en conséquence.

4
Kaylakaze

La meilleure solution à ma situation était:

el.addEventListener('mousedown', (event) => {
  clickTime = new Date()
})

el.addEventListener('click', (event) => {
  if (new Date() - clickTime < 150) {
    // do something
  }
})

Cela donne à l'utilisateur 150 ms pour libérer, s'ils prennent plus de 150 ms, c'est considéré comme une pause, plutôt qu'un clic

2
Kim T

Comme il s'agit d'événements différents, vous ne pouvez pas annuler onclick de onmouseup. Si vous appelez preventDefault ou cancelBubble, ou peu importe, vous empêchez le traitement de l'événement onmouseup. L'événement onclick est toujours en attente et doit encore être déclenché, pour ainsi dire.

Ce dont vous avez besoin est votre propre drapeau booléen, par exemple. isDragging. Vous pouvez définir ce paramètre sur true lorsque le glissement commence (par exemple, dans onmousedown ou autre). 

Mais si vous redéfinissez la valeur false directement à partir de onmouseup, vous ne ferez plus glisser lorsque vous recevez votre événement onclick (isDragging == false), car onmouseup est déclenché avant onclick.

Vous devez donc utiliser un court délai (par exemple, setTimeout(function() {isDragging = false;}, 50);). Ainsi, lorsque votre événement onclick est déclenché, isDragging reste toujours true et votre gestionnaire d'événements onclick peut simplement avoir if(isDragging) return false; avant qu'il ne fasse autre chose.

1
Lee Kowalkowski

C'est peut-être possible, mais je ne suis pas sûr que vous puissiez gérer ce type de gestion d'evt avec jQuery. Cet exemple de code ne devrait pas être très éloigné de vos attentes ou du moins vous indiquer une direction.

function AddEvent(id, evt_type, ma_fonction, phase) {
    var oElt = document.getElementById(id);
    // modèle W3C mode bubbling
    if( oElt.addEventListener ) {
        oElt.addEventListener(evt_type, ma_fonction, phase);
    // modèle MSIE
    } else if( oElt.attachEvent ) {
        oElt.attachEvent('on'+evt_type, ma_fonction);
    }
    return false;
}

function DelEvent(id, evt_type, ma_fonction, phase) {
    var oElt = document.getElementById(id);
    // modèle W3C mode bubbling
    if( oElt.removeEventListener ) {
        oElt.removeEventListener(evt_type, ma_fonction, phase);
    // modèle MSIE
    } else if( oElt.detachEvent ) {
        oElt.detachEvent('on'+evt_type, ma_fonction);
    }
    return false;
}

    var mon_id = 'test';
    var le_type_evt = "mouseup";
    var flux_evt = false; // prevent bubbling
    var action_du_gestionnaire = function(e) {
        alert('evt mouseup on tag <div>');

        // 1ère méthode : DOM Lev 2
        // W3C
            if ( e.target )
                e.target.removeEventListener(le_type_evt, arguments.callee, flux_evt);
        // MSIE
            else if ( e.srcElement )
                e.srcElement.detachEvent('on'+le_type_evt, arguments.callee);

        // 2ème méthode DOM Lev2
        // DelEvent(mon_id, le_type_evt, action_du_gestionnaire, flux_evt);
    };


    AddEvent(mon_id, le_type_evt, action_du_gestionnaire, flux_evt);
0
hornetbzz

j'ai récemment fait face au même problème. Voici ma solution :)

    initDrag: function (element) {
        var moved = false,
            target = null;

        function move(e) {
            // your move code
            moved = true;
        };

        function end(e) {
            var e = e || window.event,
                current_target = e.target || e.srcElement;

            document.onmousemove = null;
            document.onmouseup = null;

            // click happens only if mousedown and mouseup has same target
            if (moved && target === current_target) 
                element.onclick = click;

            moved = false;
        };

        function click(e) {
            var e = e || window.event;

            e.preventDefault();
            e.stopPropagation();

            // this event should work only once
            element.onclick = null;
        };

        function init(e) {
            var e = e || window.event;
            target = e.target || e.srcElement;

            e.preventDefault();

            document.onmousemove = move;
            document.onmouseup = end;
        };

        element.onmousedown = init;
    };
0
starl1ng

Ceci est ma solution pour glisser et cliquer sur le même élément.

$('selector').on('mousedown',function(){
  setTimeout(function(ele){ele.data('drag',true)},100,$(this));
}).on('mouseup',function(){
  setTimeout(function(ele){ele.data('drag',false)},100,$(this));
}).on('click',function(){
  if($(this).data('drag')){return;}
  // put your code here
});
0
Quyền Lê

la solution que j'utilise est la suivante:

var disable_click = false;

function click_function(event){
    if (!disable_click){
        // your code
    }
}

function mouse_down_function(event){
    disable_click = false; // will enable the click everytime you click
    // your code
}

function mouse_up_function(event){
    // your code
}

function mouse_drag_function(event){
    disable_click = true; // this will disable the click only when dragging after clicking
   // your code
}

attachez chaque fonction à l'événement approprié en fonction du nom!

0
Mohamed Benkedadra

Ma solution ne nécessite pas de variables globales ni de délais d'attente ni de changement d'éléments HTML, mais simplement de jquery, qui a sûrement un équivalent dans js simple.

Je déclare une fonction pour onClick

function onMouseClick(event){
     // do sth
}

Je déclare une fonction pour MouseDown (vous pouvez également faire la même chose avec la souris levée) pour décider de gérer un événement onclick ou non.

function onMouseDown(event){
     // some code to decide if I want a click to occur after mouseup or not
    if(myDecision){
        $('#domElement').on("click", onMouseClick);
    }
    else $('#domElement').off("click");
} 

Remarque rapide: vous devez vous assurer que
$('#domElement').on("click", onMouseClick); n'est pas exécuté plusieurs fois. Il me semble que si cela se produit, onMouseClick sera également appelé plusieurs fois. 

0
Tom