Tengo elementos en la página que se pueden arrastrar con jQuery. ¿Estos elementos tienen un evento de clic que navega a otra página (enlaces normales, por ejemplo)?
¿Cuál es la mejor manera de evitar que el clic se dispare al soltar dicho elemento mientras se permite que el clic no se arrastre y suelte?
Tengo este problema con los elementos ordenables, pero creo que es bueno tener una solución para arrastrar y soltar en general.
He resuelto el problema por mí mismo. Después de eso, descubrí que existe la misma solución para Scriptaculous , pero tal vez alguien tenga una mejor manera de lograrlo.
javascript
jquery
events
drag-and-drop
jquery-ui-sortable
Alexander Yanovets
fuente
fuente
Respuestas:
Una solución que me funcionó bien y que no requiere un tiempo de espera: (sí, soy un poco pedante ;-)
Agrego una clase de marcador al elemento cuando se inicia el arrastre, por ejemplo, 'noclick'. Cuando se suelta el elemento, se activa el evento de clic; más precisamente, si el arrastre finaliza, en realidad no es necesario colocarlo en un objetivo válido. En el controlador de clic, elimino la clase de marcador si está presente; de lo contrario, el clic se maneja normalmente.
$('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 } });
fuente
stop: function(event, ui) { $(this).removeClass('noclick'); }
en el controlador arrastrable? De lo contrario, no todos los procesos de arrastrar y soltar en otro lugar frustrarán el siguiente clic en "su selector".La solución es agregar un controlador de clic que evitará que el clic se propague al inicio del arrastre. Y luego elimine ese controlador después de que se realice la caída. La última acción debe retrasarse un poco para que funcione la prevención de clics.
Solución para ordenar:
... .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); } ... })
Solución para arrastrar:
... .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); } ... })
fuente
Tuve el mismo problema e intenté varios enfoques y ninguno funcionó para mí.
Solución 1
$('.item').click(function(e) { if ( $(this).is('.ui-draggable-dragging') ) return false; });
no hace nada por mi Se hace clic en el elemento después de que se realiza el arrastre.
Solución 2 (por Tom de Boer)
$('.item').draggable( { stop: function(event, ui) { $( event.originalEvent.target).one('click', function(e){ e.stopImmediatePropagation(); } ); } });
Esto funciona bien, pero falla en un caso, cuando estaba en pantalla completa al hacer clic:
var body = $('body')[0]; req = body.requestFullScreen || body.webkitRequestFullScreen || body.mozRequestFullScreen; req.call(body);
Solución 3 (por 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); } })
Esto no funciona para mi.
Solución 4- el único que funcionó bien
$('.item').draggable( { }); $('.item').click(function(e) { });
Sí, eso es todo, el orden correcto hace el truco, primero debe vincular el evento draggable () y luego hacer clic (). Incluso cuando puse el código de alternancia de pantalla completa en el evento click (), todavía no pasaba a pantalla completa al arrastrar. ¡Perfecto para mi!
fuente
Me gustaría agregar a esto que parece que la prevención del evento de clic solo funciona si el evento de clic se define DESPUÉS del evento que se puede arrastrar o ordenar. Si el clic se agrega primero, se activa al arrastrar.
fuente
Realmente no me gusta usar temporizadores o prevenir, así que lo que hice fue esto:
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; }
Roca sólida..
fuente
versión de lex82 pero para .sortable ()
start: function(event, ui){ ui.item.find('.ui-widget-header').addClass('noclick'); },
y es posible que solo necesite:
start: function(event, ui){ ui.item.addClass('noclick'); },
y esto es lo que estoy usando para alternar:
$("#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"); } });
fuente
Una posible alternativa para la respuesta de Sasha sin evitar el incumplimiento:
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); },
fuente
En jQuery UI, los elementos que se arrastran reciben la clase "ui-draggable-dragging".
Por lo tanto, podemos usar esta clase para determinar si hacer clic o no, solo retrasar el evento.
No es necesario utilizar las funciones de devolución de llamada "iniciar" o "detener", simplemente haga lo siguiente:
$('#foo').on('mouseup', function () { if (! $(this).hasClass('ui-draggable-dragging')) { // your click function } });
Esto se activa desde "mouseup", en lugar de "mousedown" o "click", por lo que hay un ligero retraso, puede que no sea perfecto, pero es más fácil que otras soluciones sugeridas aquí.
fuente
Después de leer esto y algunos hilos, esta fue la solución con la que fui.
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; } });
fuente
En mi caso funcionó así:
$('#draggable').draggable({ start: function(event, ui) { $(event.toElement).one('click', function(e) { e.stopPropagation(); }); } });
fuente
¿Ha intentado deshabilitar el enlace usando event.preventDefault (); en el evento de inicio y volver a habilitarlo en el evento de arrastre detenido o en el evento de caída usando desvincular
fuente
Solo una pequeña arruga para agregar a las respuestas dadas anteriormente. Tuve que hacer un div que contenga un elemento de SalesForce que se pueda arrastrar, pero el elemento de SalesForce tiene una acción onclick definida en el html a través de algún gobbledigook de VisualForce.
Obviamente, esto viola la regla "definir acción de clic después de la acción de arrastre", así que como solución, redefiní la acción del elemento SalesForce para que se active "onDblClick", y usé este código para el div contenedor:
$(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'); } });
El evento de clic del padre esencialmente oculta la necesidad de hacer doble clic en el elemento secundario, dejando intacta la experiencia del usuario.
fuente
Intenté así:
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); } });
fuente
para mí ayudó a pasar el ayudante en el objeto de opciones como:
.sortable({ helper : 'clone', start:function(), stop:function(), ..... });
Parece que la clonación del elemento dom que se arrastra impidió el burbujeo del evento. No pude evitarlo con ningún eventPropagation, burbujeo, etc. Esta fue la única solución que me funcionó.
fuente
Los eventos onmousedown y onmouseup funcionaron en uno de mis proyectos más pequeños.
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();" />
Sí, lo sé. No es la forma más limpia, pero entiendes la idea.
fuente
la solución más sencilla y robusta? simplemente cree un elemento transparente sobre su arrastrable.
.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(); } });
fuente