Предотвращение события щелчка с помощью перетаскивания jQuery


85

У меня есть элементы на странице, которые можно перетаскивать с помощью jQuery. Есть ли у этих элементов событие click, которое переходит на другую страницу (например, обычные ссылки).

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

У меня есть проблема с сортируемыми элементами, но я думаю, что хорошо иметь решение для общего перетаскивания.

Решил для себя проблему. После этого я обнаружил, что такое же решение существует для Scriptaculous , но, возможно, у кого-то есть лучший способ добиться этого.


Похожие / дубликаты: stackoverflow.com/questions/3486760/…
Райан,

Ответы:


88

Решение, которое хорошо сработало для меня и не требует тайм-аута: (да, я немного педантичен ;-)

Я добавляю класс маркера к элементу при запуске перетаскивания, например, «noclick». Когда элемент отбрасывается, запускается событие щелчка - точнее, если перетаскивание заканчивается, на самом деле его не нужно отбрасывать на допустимую цель. В обработчике кликов я удаляю класс маркера, если он есть, иначе щелчок обрабатывается нормально.

$('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
Полезно! У меня не сработало за один раз, вероятно, потому, что я ошибался в предыдущих попытках с тем же самым, и классы оказались на другом элементе, чем тот, на который щелкнули ... но в любом случае я использовал глобальную переменную (которая на этом чрезвычайно простая страница была в порядке), и это тоже работало довольно хорошо.
MSpreij

2
Это привело меня к моему решению, я просто использовал функцию данных jQuery вместо класса. Спасибо.
Уильям

3
но браузер не будет запускать событие щелчка каждый раз, когда вы отбрасываете элемент
puchu

6
Чтобы сохранить настройку класса или данных, в обработчике события клика вы также можете использовать: if (! $ (This) .is ('. Ui-draggable-dragging')) {... click action}
ChrisV

1
Ой, разве вы не хотите stop: function(event, ui) { $(this).removeClass('noclick'); }перетаскиваемого обработчика? В противном случае не каждое перетаскивание в другое место помешает следующему щелчку по «вашему селектору».
Bob Stein

41

Решение состоит в том, чтобы добавить обработчик щелчка, который предотвратит распространение щелчка при начале перетаскивания. А затем удалите этот обработчик после выполнения сброса. Последнее действие следует немного отложить, чтобы сработала функция предотвращения кликов.

Решение для сортируемого:

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

Решение для перетаскиваемого:

...
.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
Кажется, это не могло предотвратить событие щелчка для меня, я использую jquery 1.9.1 и jquery.ui 1.10.3
Аарон

1
странно, что ты сам ответил на свой вопрос, а он даже не работает, Саша, лол.

@aaron, этот ответ ниже работает: http://stackoverflow.com/a/4572831/119765
lol

Другой простой ответ - просто поместите обработчик jQuery .click () после .draggable () stackoverflow.com/questions/18032136/…
JxAxMxIxN

1
если вы передадите вспомогательный атрибут атрибута со значением 'clone', он предотвратит запуск события для элемента, отсортированного перетаскиванием .. {helper: 'clone', start, stop ... etc},
Luchux

12

У меня была та же проблема, и я пробовал несколько подходов, и ни один из них не помог мне.

Решение 1

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

ничего не делает для меня. По элементу щелкают после завершения перетаскивания.

Решение 2 (от Тома де Бура)

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

Это работает нормально, но не работает в одном случае - когда я переходил в полноэкранный режим onclick:

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

Решение 3 (Саша Яновец)

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

У меня это не работает.

Решение 4 - единственное, что работало нормально

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

Да, вот и все - правильный порядок помогает - сначала вам нужно привязать событие draggable (), затем click (). Даже когда я помещал код переключения полноэкранного режима в событие click (), он все равно не переходил в полноэкранный режим при перетаскивании. Отлично для меня!


1
У меня сработало только одно решение (в IE11 / Edge) 4. Спасибо!
Саймон

1
№4 - для меня правильное и простое решение, без использования классов или переменных.
electrotype

11

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


9

Мне не очень нравится использовать таймеры или предотвращение, поэтому я сделал следующее:

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

Скала..


Для меня это было лучшим решением. У меня возникли проблемы с получением событий щелчка для работы с пользовательским интерфейсом jquery, который можно сортировать в мобильном веб-браузере с помощью jquery.ui.touch.punch.js, но это решило проблему довольно элегантно.
Godsmith

3

версия lex82, но для .sortable ()

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

и вам может понадобиться только:

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

и вот что я использую для переключения:

$("#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
Похоже, что пользовательский интерфейс jquery добавляет класс ui-sortable-helper к сортируемому элементу. Итак, вы можете опустить класс noclick и просто сделать if ($ (this) .hasClass ('ui-sortable-helper')). Более кратко
Мэтт Де Леон

3

Возможный вариант ответа Саши без предотвращения дефолта:

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

В пользовательском интерфейсе jQuery перетаскиваемым элементам присваивается класс «ui-draggable-dragging».
Таким образом, мы можем использовать этот класс, чтобы определить, щелкать или нет, просто отложив событие.
Вам не нужно использовать функции обратного вызова "start" или "stop", просто выполните:

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

Это запускается с помощью «mouseup», а не «mousedown» или «click» - поэтому есть небольшая задержка, может быть не идеально - но это проще, чем другие решения, предлагаемые здесь.


1

Прочитав это и несколько тем, я выбрал это решение.

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

В моем случае это работало так:

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

0

Вы пробовали отключить ссылку с помощью event.preventDefault (); в событии запуска и повторно включить его в событии остановки перетаскивания или событии отпускания с помощью отмены привязки?


0

Немного морщин, чтобы добавить к приведенным выше ответам. Мне пришлось сделать div, который содержит перетаскиваемый элемент SalesForce, но у элемента SalesForce есть действие onclick, определенное в html через какой-то gobbledigook VisualForce.

Очевидно, это нарушает правило «определить действие щелчка после действия перетаскивания», поэтому в качестве обходного пути я переопределил действие элемента SalesForce, которое должно запускаться «onDblClick», и использовал этот код для контейнера 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');
    }
});

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


0

Я пробовал вот так:

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

для меня помог передать помощника в объекте параметров как:

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

Кажется, что клонирование перетаскиваемого элемента dom предотвратило всплытие события. Я не мог избежать этого с помощью любого eventPropagation, всплывания и т. Д. Это было единственное рабочее решение для меня.


0

События onmousedown и onmouseup работали в одном из моих небольших проектов.

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

Да, я знаю. Не самый чистый способ, но идею вы поняли.


0

самое простое и надежное решение? просто создайте прозрачный элемент поверх перетаскиваемого.

.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();
        }
    });
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.