Отмена события щелчка в обработчике события mouseup - PullRequest
26 голосов
/ 27 декабря 2011

При написании кода перетаскивания, я хотел бы отменить события нажатия в моем обработчике мышки. Я подумал, что предотвращение дефолта должно сработать, но событие click все еще происходит.

Есть ли способ сделать это?


Это не работает:

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

Ответы [ 14 ]

17 голосов
/ 29 ноября 2013

Использование фазы захвата события

Поместите элемент вокруг элемента, для которого вы хотите отменить событие click, и добавьте к нему обработчик события захвата.

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

Что происходит:

Перед событием нажатия на buttonзапускается событие click на окружающем div срабатывании, потому что оно зарегистрировалось для фазы захвата вместо фазы пузырьков.

Затем обработчик captureClick останавливает распространение своего события click и предотвращаетобработчик click на вызываемой кнопке.Именно то, что вы хотели.Затем он удаляет себя для очистки.

Capturing vs. Bubbling:

Фаза захвата вызывается от корня DOM до листьев, в то время как фаза пузырьков идет от листьев до корня (см.: замечательное объяснение порядка событий ).

jQuery всегда добавляет события в фазу пузырьков, поэтому нам нужно использовать чистый JS, чтобы добавить событие захвата специально к фазе захвата.

Имейте в виду, что IE представил модель захвата событий W3C с IE9, поэтому это не будет работать с IE <9. </p>


С текущим API событий вы не можете добавить новыйобработчик события для элемента DOM перед другим, который уже был добавлен.Нет параметра приоритета и нет безопасного кросс-браузерного решения для изменения списка прослушивателей событий .

11 голосов
/ 18 июня 2014

Есть решение!

Этот подход работает для меня очень хорошо (по крайней мере, в Chrome):

на mousedown Я добавляю класс к элементу, который в данный момент перемещаетсяи на mouseup я удаляю класс.

Все, что делает этот класс, устанавливает pointer-events:none

Каким-то образом это заставляет его работать, и событие щелчка не запускается.

5 голосов
/ 19 января 2012

У меня была такая же проблема, и я тоже не нашел решения.Но я придумал хак, который, кажется, работает.

Поскольку обработчик onMouseUp, по-видимому, не может отменить щелчок по ссылке с помощью protectDefault или stopEvent или чего-либо еще, нам нужно сделать так, чтобы ссылка отменялась сама.Это можно сделать, написав атрибут onclick, который возвращает false для тега a, когда начинается перетаскивание, и удаляя его, когда перетаскивание заканчивается.

И поскольку обработчики onDragEnd или onMouseUp запускаются до того, как браузер интерпретирует щелчок, нам нужно проверить, где заканчивается перетаскивание, какая ссылка нажата и так далее.Если он заканчивается за пределами перетаскиваемой ссылки (мы перетащили ссылку, чтобы курсор больше не находился на ссылке), мы удаляем обработчик onclick в onDragEnd;но если он заканчивается там, где курсор находится на перетаскиваемой ссылке (щелчок будет инициирован), мы позволяем обработчику onclick удалить себя.Достаточно сложно, верно?

НЕ ПОЛНЫЙ КОД, но просто чтобы показать вам идею:

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

Я надеюсь, что это даст вам представление о том, как этого можно достичь.Как я уже сказал, это не полное решение, просто объяснение концепции.

4 голосов
/ 21 мая 2012

Проблема в том, что есть элемент. Нужно реагировать на клики. Это также необходимо перетащить. Однако, когда он перетаскивается, он не должен вызывать щелчок при отбрасывании.

Немного поздно, но, возможно, это поможет кому-то еще. Создайте глобальную переменную с именем "noclick" или что-то еще и установите для нее значение false. При перетаскивании элемента установите для noclick значение true. В вашем обработчике щелчков, если noclick равен true, установите для него значение false, а затем предотвратите дефолт, верните false, stopPropagation и т. Д. Это не будет работать в Chrome, хотя Chrome уже имеет желаемое поведение. Просто сделайте так, чтобы функция перетаскивания установила для noclick значение true, только если браузер не Chrome.

Ваш обработчик кликов все равно будет запущен, но, по крайней мере, у него есть способ узнать, что он только что вернулся с перетаскивания, и вести себя соответственно.

3 голосов
/ 24 июня 2018

Лучшее решение для моей ситуации было:

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

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

Это дает пользователю 150 мс для освобождения, если он занимает больше 150 мс, это считается паузой, а не щелчком

1 голос
/ 18 июня 2012

Поскольку это разные события, вы не можете отменить onclick с onmouseup, если вы позвоните preventDefault или cancelBubble, или что-то еще, вы останавливаете событие onmouseup от дальнейшей обработки. Событие onclick еще не завершено, но, так сказать, еще не запущено.

Вам нужен собственный логический флаг, например, isDragging. Вы можете установить значение true, когда начинается перетаскивание (например, в пределах onmousedown или что-либо еще).

Но если вы сбросите значение false прямо из onmouseup, вы больше не будете перетаскивать при получении события onclick (isDragging == false), потому что onmouseup срабатывает раньше, чем onclick.

Итак, вам нужно использовать короткий тайм-аут (например, setTimeout(function() {isDragging = false;}, 50);), чтобы при возникновении события onclick isDragging по-прежнему было true, а ваш обработчик события onclick мог просто иметь if(isDragging) return false;, прежде чем он сделает что-нибудь еще.

0 голосов
/ 09 сентября 2018

Мое решение не требует глобальных переменных или тайм-аутов или изменения html-элементов, просто jquery, который, безусловно, имеет некоторый уровень в простой js.

Я объявляю функцию для onClick

function onMouseClick(event){
     // do sth
}

Я объявляю функцию для MouseDown (вы также можете сделать то же самое при наведении мыши), чтобы решить, обрабатывать событие onclick или нет

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

Краткое примечание: убедитесь, что
$('#domElement').on("click", onMouseClick); не выполняется несколько раз. Сдается мне, что в случае, если это onMouseClick будет вызываться несколько раз.

0 голосов
/ 26 апреля 2018

Я использую следующее решение:

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
}

присоедините каждую функцию к соответствующему событию в соответствии с именем!

0 голосов
/ 27 июня 2017

Это мое решение для перетаскивания и нажмите на тот же элемент.

$('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 голосов
/ 29 июля 2012

Я недавно столкнулся с той же проблемой. Вот мое решение:)

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