Você consegue detectar “arrastar” no jQuery?

109

Eu tenho um latejante que aparece quando um usuário clica em um link.

O problema é que esse mesmo link pode ser clicado e arrastado para ser reorganizado. Nesse caso, eu não precisaria que o latejante aparecesse. Ele só precisa aparecer se estiver realmente esperando para ir a algum lugar.

Como posso, com jQuery, criar um ouvinte de evento que só permitiria que um throbber apareça se for um clique para um link , e não um clique e arraste?

Viagem
fonte
Talvez OOT? Eu tentei várias abordagens para arrastar e soltar com jQuery incluindo jQuery UI, meu próprio código com mousedown, mouseup, mousemove events e a que funcionou melhor é github.com/haberman/jdragdrop .
Rafael Vega
Aqui está outra solução simples, sem qualquer estrutura pesada. blog.blakesimpson.co.uk/read/…
Chemical Programmer

Respostas:

225

No mousedown, comece a definir o estado, se o evento mousemove for disparado grave-o, finalmente no mouseup, verifique se o mouse se moveu. Se mudou, estamos arrastando. Se não mexemos, é um clique.

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

Aqui está uma demonstração: http://jsfiddle.net/W7tvD/1399/

Simen Echholt
fonte
1
ei ... você pode me ajudar com este stackoverflow.com/questions/19703617/…
HIRA THAKUR
2
Você pode querer adicionar um limite de arrasto para que seus cliques não sejam interpretados como arrasta no elemento "a". Basta procurar uma certa quantidade de mudança em um evento de movimento do mouse xey.
Ash Blue de
Impressionante, logo após o vento desvincular o mouse, coloquei meu código para agir assim que o início do arrastamento foi detectado. Te agradece!
Valamas
3
Descobri que em alguns casos e / ou navegadores, mousemoveainda será disparado pelo menos uma vez, mesmo sem nenhum movimento, então mudei para incrementar a variável no movimento e, em seguida, verifiquei um certo limite em mouseup jsfiddle.net/W7tvD/1649 Funciona como um encanto para mim agora, obrigado!
halfbit
27

Por algum motivo, as soluções acima não estavam funcionando para mim. Eu fui com o seguinte:

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

Você pode ajustar o limite de distância para o que quiser, ou mesmo levá-lo para zero.

novato
fonte
Melhor resposta, me ajudou muito. Apenas um pensamento: Math.sqrt não é realmente necessário, trabalhar melhor com distâncias quadradas (sqrt é lento, pode afetar a UX).
Bruno Ferreira
1
sqrtnão é lento e não afetará a UX, nem mesmo se você fez isso centenas de vezes.
NateS
Sim, se preocupe com a raiz quadrada desnecessária no loop interno de seu mecanismo de física, não com o gerenciador de cliques do jquery ...
PeterT
19

Com o jQuery UI basta fazer isso!

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

  },
  drag: function() {

  },
  stop: function() {

  }
});
GaidenFocus
fonte
6
Você precisa do jquery-ui, mas é a maneira mais fácil.
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 solução usa as funções "on" e "off" para vincular um evento de desassociação de um mousemove (vincular e desvincular estão obsoletos). Você também pode detectar a mudança nas posições xey do mouse entre dois eventos de movimento do mouse.

jnaklaas
fonte
3

Experimente o seguinte: mostra quando está no estado 'arrastado'. ;) link de violino

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

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

Eu me desviei da resposta aceita para apenas correr quando o clique está sendo MANTIDO para baixo e arrastado .

Minha função estava funcionando quando eu não estava segurando o mouse. Este é o código atualizado se você também quiser esta funcionalidade:

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
fonte
1

Para esta maneira mais simples é tocar iniciar, tocar mover e tocar final. Isso está funcionando tanto para PC quanto para dispositivo de toque, basta verificar na documentação do jquery e espero que esta seja a melhor solução para você. boa sorte

Anup
fonte
1

Plugin jQuery baseado na resposta de Simen Echholt. Eu chamei de clique ú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);
        });
    });
};

Em uso:

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

^^

ipfaffen
fonte
1

Certifique-se de definir o atributo draggable do elemento como false para que você não tenha efeitos colaterais ao ouvir eventos de mouseup:

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

Então, você pode 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
fonte
0

Você precisa definir um cronômetro. Quando o cronômetro expirar, inicie o throbber e registre o clique. Quando ocorre o arrasto, limpe o cronômetro para que ele nunca termine.

Diodeus - James MacFarlane
fonte
1
..muito inteligente, como você detecta um arrasto?
Viagem de
1
quando você configura o Draggable, deve haver um evento onDrag. Ligue lá em cima.
Diodeus - James MacFarlane
0

Se você estiver usando jQueryUI - há um evento onDrag. Se não estiver, conecte seu ouvinte a mouseup (), e não a click ().

Quasipickle
fonte
1
Como o mouseup diferencia entre arrastar e clicar?
Viagem de
Hmmm - sim, acho que não pensei o suficiente. Talvez você possa ter um ouvinte conectado ao mouse, que registra as coordenadas do mouse. Então, se as coordenadas do mouse para cima forem as mesmas (ou realmente próximas), considere um clique.
Quasipickle
Isso não funciona: stackoverflow.com/questions/12231147/…
Chuck Le Butt,
A resposta também tem 4 anos - considere isso.
Quasipickle 02 de
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
fonte
0

Você não precisa configurar a variável, você pode apenas definir se ela está se movendo no atributo de dados

$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
fonte
0

Eu precisava de uma função que sempre acompanhasse a posição do mouse e detectasse arrastar para a esquerda, direita, cima e baixo. Também não é acionado com um clique, mas precisa de um movimento mínimo de 15 px

/**
 * 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
fonte