¿Puedes detectar "arrastrar" en jQuery?

109

Tengo un latido que aparece cuando un usuario hace clic en un enlace.

El problema es que se puede hacer clic en ese mismo enlace y arrastrarlo para reorganizarlo. En este caso, no necesitaría que apareciera el palpitante. Solo necesita aparecer si realmente está esperando para ir a alguna parte.

¿Cómo puedo, con jQuery, crear un detector de eventos que solo permita que aparezca un palpitante si es un clic a través de un enlace , y no un clic y arrastrar?

Viaje
fuente
¿Quizás OOT? Probé varios enfoques para arrastrar y soltar con jQuery, incluida la interfaz de usuario de jQuery, mi propio código con eventos mousedown, mouseup, mousemove y el que ha funcionado mejor es github.com/haberman/jdragdrop .
Rafael Vega
Aquí hay otra solución simple sin ningún marco pesado. blog.blakesimpson.co.uk/read/…
Programador químico

Respuestas:

225

En mousedown, comience a establecer el estado, si el evento mousemove se activa, regístrelo, finalmente en mouseup, verifique si el mouse se movió. Si se movió, lo hemos estado arrastrando. Si no nos hemos movido, es un clic.

var isDragging = false;
$("a")
.mousedown(function() {
    isDragging = false;
})
.mousemove(function() {
    isDragging = true;
 })
.mouseup(function() {
    var wasDragging = isDragging;
    isDragging = false;
    if (!wasDragging) {
        $("#throbble").toggle();
    }
});

Aquí hay una demostración: http://jsfiddle.net/W7tvD/1399/

Simen Echholt
fuente
1
hey ... ¿puedes ayudarme con esto? stackoverflow.com/questions/19703617/…
HIRA THAKUR
2
Es posible que desee agregar un umbral de arrastre para que sus clics no se interpreten como arrastres en el elemento "a". Solo busque una cierta cantidad de cambio en un evento xey mousemove.
Ash Blue
Impresionante, justo después de que el viento desenlazara mousemove puse mi código para que actúe tan pronto como se detectó el inicio del arrastre. ¡gracias!
Valamas
3
Descubrí que, en algunos casos y / o navegadores, mousemovese seguirán disparando al menos una vez incluso sin ningún movimiento, así que lo cambié para incrementar la variable en el movimiento y luego verifiqué un cierto umbral en mouseup jsfiddle.net/W7tvD/1649 Funciona como un encanto para mí ahora, ¡gracias!
Halfbit
27

Por alguna razón, las soluciones anteriores no me funcionaron. Fui con lo siguiente:

$('#container').on('mousedown', function(e) {
    $(this).data('p0', { x: e.pageX, y: e.pageY });
}).on('mouseup', function(e) {
    var p0 = $(this).data('p0'),
        p1 = { x: e.pageX, y: e.pageY },
        d = Math.sqrt(Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2));

    if (d < 4) {
        alert('clicked');
    }
})

Puede ajustar el límite de distancia a lo que quiera, o incluso llevarlo hasta cero.

recién llegado
fuente
La mejor respuesta, me ayudó mucho. Solo un pensamiento: Math.sqrt no es realmente necesario, es mejor trabajar con distancias cuadradas (sqrt es lento, podría afectar la UX).
Bruno Ferreira
1
sqrtno es lento y no afectará a la UX, ni siquiera si lo hiciste cientos de veces.
NateS
Sí, preocúpese por la raíz cuadrada innecesaria en el bucle interno de su motor de física, no por su controlador de clic jquery ...
PeterT
19

¡Con jQuery UI simplemente haz esto!

$( "#draggable" ).draggable({
  start: function() {

  },
  drag: function() {

  },
  stop: function() {

  }
});
GaidenFocus
fuente
6
Necesita jquery-ui, pero es la forma más sencilla.
Ortomala Lokni
5
$(".draggable")
.mousedown(function(e){
    $(this).on("mousemove",function(e){
        var p1 = { x: e.pageX, y: e.pageY };
        var p0 = $(this).data("p0") || p1;
        console.log("dragging from x:" + p0.x + " y:" + p0.y + "to x:" + p1.x + " y:" + p1.y);
        $(this).data("p0", p1);
    });
})
.mouseup(function(){
    $(this).off("mousemove");
});

Esta solución utiliza las funciones "on" y "off" para vincular y desvincular un evento mousemove (vincular y desvincular están en desuso). También puede detectar el cambio en las posiciones xey del mouse entre dos eventos mousemove.

jnaklaas
fuente
3

Pruebe esto: muestra cuándo está el estado 'arrastrado'. ;) enlace de violín

$(function() {
    var isDragging = false;
    $("#status").html("status:");
    $("a")
    .mousedown(function() {
        $("#status").html("status: DRAGGED");        
    })
    .mouseup(function() {
        $("#status").html("status: dropped");   
    });

    $("ul").sortable();
});
syC
fuente
2

Me desvié de la respuesta aceptada para ejecutar solo cuando el clic se mantiene presionado y arrastrado .

Mi función se estaba ejecutando cuando no estaba presionando el mouse. Aquí está el código actualizado si también desea esta funcionalidad:

var isDragging = false;
var mouseDown = false;

$('.test_area')
    .mousedown(function() {
        isDragging = false;
        mouseDown = true;
    })
    .mousemove(function(e) {
        isDragging = true;

        if (isDragging === true && mouseDown === true) {
            my_special_function(e);
        }
     })
    .mouseup(function(e) {

        var wasDragging = isDragging;

        isDragging = false;
        mouseDown = false;

        if ( ! wasDragging ) {
            my_special_function(e);
        }

    }
);
Jack
fuente
1

Para esta forma más sencilla es tocar inicio, tocar mover y tocar final. Eso funciona tanto para PC como para dispositivos táctiles, solo compruébalo en la documentación de jquery y espero que esta sea la mejor solución para ti. buena suerte

Anup
fuente
1

Complemento jQuery basado en la respuesta de Simen Echholt. Lo llamé clic único.

/**
 * jQuery plugin: Configure mouse click that is triggered only when no mouse move was detected in the action.
 * 
 * @param callback
 */
jQuery.fn.singleclick = function(callback) {
    return $(this).each(function() {
        var singleClickIsDragging = false;
        var element = $(this);

        // Configure mouse down listener.
        element.mousedown(function() {
            $(window).mousemove(function() {
                singleClickIsDragging = true;
                $(window).unbind('mousemove');
            });
        });

        // Configure mouse up listener.
        element.mouseup(function(event) {
            var wasDragging = singleClickIsDragging;
            singleClickIsDragging = false;
            $(window).unbind('mousemove');
            if(wasDragging) {
                return;
            }

            // Since no mouse move was detected then call callback function.
            callback.call(element, event);
        });
    });
};

En uso:

element.singleclick(function(event) {
    alert('Single/simple click!');
});

^^

ipfaffen
fuente
1

Asegúrese de establecer el atributo arrastrable del elemento en falso para que no tenga efectos secundarios al escuchar los eventos del mouseup:

<div class="thing" draggable="false">text</div>

Entonces, puedes usar jQuery:

$(function() {
  var pressed, pressX, pressY,
      dragged,
      offset = 3; // helps detect when the user really meant to drag

  $(document)
  .on('mousedown', '.thing', function(e) {
    pressX = e.pageX;
    pressY = e.pageY;
    pressed = true;
  })
  .on('mousemove', '.thing', function(e) {
    if (!pressed) return;
    dragged = Math.abs(e.pageX - pressX) > offset ||
              Math.abs(e.pageY - pressY) > offset;
  })
  .on('mouseup', function() {
    dragged && console.log('Thing dragged');
    pressed = dragged = false;
  });
});
Marcelo Kanzaki
fuente
0

Tienes que configurar un temporizador. Cuando se agote el tiempo del temporizador, encienda el vibrador y registre el clic. Cuando se produce el arrastre, borre el temporizador para que nunca se complete.

Diodeus - James MacFarlane
fuente
1
..muy inteligente, ¿cómo detecta un arrastre?
Viaje
1
cuando configura el Draggable, debería haber un evento onDrag. Conéctelo allí.
Diodeus - James MacFarlane
0

Si está utilizando jQueryUI, hay un evento onDrag. Si no es así, adjunte su oyente a mouseup (), no haga clic en ().

Cuasipickle
fuente
1
¿Cómo diferenciaría mouseup entre arrastrar y hacer clic?
Viaje
Hmmm, sí, supongo que no lo pensé lo suficiente. Quizás pueda tener un oyente adjunto al mousedown que registre las coordenadas del mouse. Luego, si las coordenadas del mouse en mouseup son las mismas (o muy cercanas), considérelo un clic.
Quasipickle
Esto no funciona: stackoverflow.com/questions/12231147/…
Chuck Le Butt
La respuesta también tiene 4 años, considérelo.
Quasipickle
0
// here is how you can detect dragging in all four directions
var isDragging = false;
$("some DOM element").mousedown(function(e) {
    var previous_x_position = e.pageX;
    var previous_y_position = e.pageY;

    $(window).mousemove(function(event) {
        isDragging = true;
        var x_position = event.pageX;
        var y_position = event.pageY;

        if (previous_x_position < x_position) {
            alert('moving right');
        } else {
            alert('moving left');
        }
        if (previous_y_position < y_position) {
            alert('moving down');
        } else {
            alert('moving up');
        }
        $(window).unbind("mousemove");
    });
}).mouseup(function() {
    var wasDragging = isDragging;
    isDragging = false;
    $(window).unbind("mousemove");
});
Joseph
fuente
0

No tiene que configurar la variable, solo puede configurar si se está moviendo en el atributo de datos

$youtubeSlider.find('a')
    .on('mousedown', function (e) {
        $(this).data('moving', false);
    })
    .on('mousemove', function (e) {
        $(this).data('moving', true);
    })
    .on('mouseup', function (e) {
        if (!$(this).data('moving')) {
            // Open link
        }
    });
Aiphee
fuente
0

Necesitaba una función que siempre realizara un seguimiento de la posición del mouse y detectara el arrastre hacia la izquierda, derecha, arriba o abajo. Tampoco se activa al hacer clic, pero necesita un movimiento mínimo de 15px

/**
 * Check for drag when moved minimum 15px
 * Same time keep track of mouse position while dragging
 */
// Variables to be accessed outside in other functions
var dragMouseX;
var dragMouseY;
var myDragging = false; // true or false
var dragDirectionX = false; // left or right
var dragDirectionY = false; // top or bottom

$(document).on("mousedown", function(e) {
    // Reset some variables on mousedown
    var lastDirectionCheck = e.timeStamp;
    var dragStartX = e.pageX;
    var dragStartY = e.pageY;
    dragMouseX = e.pageX;
    dragMouseY = e.pageY;
    myDragging = false;
    dragDirectionX = false;
    dragDirectionY = false;

    // On the move
    $(document).on("mousemove", function(e) {
        dragMouseX = e.pageX;
        dragMouseY = e.pageY;

        // Recalculate drag direction every 200ms in case user changes his mind
        if (e.timeStamp > (lastDirectionCheck + 200)) {
            dragStartX = dragMouseX;
            dragStartY = dragMouseY;
            lastDirectionCheck = e.timeStamp;
        }

        // Check for drag when moved minimum 15px in any direction
        if (!myDragging && Math.abs(dragStartX - dragMouseX) > 15 || Math.abs(dragStartY - dragMouseY) > 15) {
            myDragging = true;
        }
        if (myDragging) {
            // Check drag direction X
            if (dragStartX > dragMouseX) dragDirectionX = 'left';
            if (dragStartX < dragMouseX) dragDirectionX = 'right';

            // Check drag direction Y
            if (dragStartY > dragMouseY) dragDirectionY = 'top';
            if (dragStartY < dragMouseY) dragDirectionY = 'bottom';

            // console.log(dragDirectionX + ' ' + dragDirectionY);
        }
    });
});

// Reset some variables again on mouseup
$(document).on("mouseup", function() {
    $(document).off("mousemove");
    myDragging = false;
    dragDirectionX = false;
    dragDirectionY = false;
});
Julesezaar
fuente