jQuery obtém a posição do mouse dentro de um elemento

165

Eu esperava criar um controle no qual um usuário pudesse clicar dentro de uma div, arrastar o mouse e soltar o mouse para indicar quanto tempo eles querem que algo seja. (Isso é para um controle de calendário, o usuário indicará a duração, no tempo, de um determinado evento)

Parece que a melhor maneira de fazer isso seria registrar um evento "mousedown" na div pai que, por sua vez, registra um evento "mousemove" na div até que um evento "mouseup" seja acionado. Os eventos "mousedown" e "mouseup" definirão o início e o fim do intervalo de tempo e, ao seguir os eventos "mousemove", posso alterar dinamicamente o tamanho do intervalo para que o usuário possa ver o que está fazendo. Baseei isso em como os eventos são criados no Google Agenda.

O problema que estou tendo é que o evento jQuery parece fornecer apenas informações confiáveis ​​sobre as coordenadas do mouse em referência a toda a página. Existe alguma maneira de discernir quais são as coordenadas em referência ao elemento pai?

Editar:

Aqui está uma foto do que estou tentando fazer: texto alternativo

Chris Dutrow
fonte

Respostas:

305

Uma maneira é usar o offsetmétodo jQuery para converter as coordenadas event.pageXe event.pageYdo evento em uma posição do mouse em relação ao pai. Aqui está um exemplo para referência futura:

$("#something").click(function(e){
   var parentOffset = $(this).parent().offset(); 
   //or $(this).offset(); if you really just want the current element's offset
   var relX = e.pageX - parentOffset.left;
   var relY = e.pageY - parentOffset.top;
});
jball
fonte
2
Sim, é uma ótima idéia se o layout do elemento pai não puder / não puder ser alterado!
Pointy
2
Esta resposta leva em consideração o preenchimento / bordas?
LeeGee
10
De acordo com os documentos, o deslocamento não considera "bordas, margens ou preenchimentos definidos no elemento do corpo". Não encontrei um problema com a minha resposta postada em uso, mas pode ser bom verificar as que estão sendo definidas.
jball
2
Isso não tem que funcionar. offset()também é relativo; portanto, se o pai for filho de outro elemento, isso dará um resultado errado. Use em position()vez disso.
Flash Thunder
1
@FlashThunder offset () não é relativo, pelo menos não de acordo com os documentos. Também deve ser uma prática padrão não ter margens ou preenchimento no elemento do corpo.
James Watkins
18

Para obter a posição do clique em relação ao elemento clicado atual
Use este código

$("#specialElement").click(function(e){
    var x = e.pageX - this.offsetLeft;
    var y = e.pageY - this.offsetTop;
}); 
Sajjad Ashraf
fonte
2
Sua resposta me ajudou BTW, a resposta aceita não. Obrigado :) O que realmente usei é este: Em vez disso, var y = e.pageY - $(this).offset().top;sua resposta me levou ao caminho certo.
Solomon Closson
11

Eu uso esse pedaço de código, é muito bom :)

    <script language="javascript" src="http://code.jquery.com/jquery-1.4.1.js" type="text/javascript"></script>
<script language="javascript">
$(document).ready(function(){
    $(".div_container").mousemove(function(e){
        var parentOffset = $(this).parent().offset();
        var relativeXPosition = (e.pageX - parentOffset.left); //offset -> method allows you to retrieve the current position of an element 'relative' to the document
        var relativeYPosition = (e.pageY - parentOffset.top);
        $("#header2").html("<p><strong>X-Position: </strong>"+relativeXPosition+" | <strong>Y-Position: </strong>"+relativeYPosition+"</p>")
    }).mouseout(function(){
        $("#header2").html("<p><strong>X-Position: </strong>"+relativeXPosition+" | <strong>Y-Position: </strong>"+relativeYPosition+"</p>")
    });
});
</script>
Maarten Hartman
fonte
9

A resposta @AbdulRahim está quase correta. Mas sugiro a função abaixo como substituta (para referência futura):

function getXY(evt, element) {
                var rect = element.getBoundingClientRect();
                var scrollTop = document.documentElement.scrollTop?
                                document.documentElement.scrollTop:document.body.scrollTop;
                var scrollLeft = document.documentElement.scrollLeft?                   
                                document.documentElement.scrollLeft:document.body.scrollLeft;
                var elementLeft = rect.left+scrollLeft;  
                var elementTop = rect.top+scrollTop;

                x = evt.pageX-elementLeft;
                y = evt.pageY-elementTop;

                return {x:x, y:y};
            }




            $('#main-canvas').mousemove(function(e){
                var m=getXY(e, this);
                console.log(m.x, m.y);
            });
Paulo Bueno
fonte
1
Melhoria brilhante, salvou o meu dia.
Bud Damyanov
quase, adicionar margem para a tela e você está fora pela margem
Benn
Este é um ótimo código, muito obrigado!
Stefan
7

Esta solução suporta todos os principais navegadores, incluindo o IE. Ele também cuida da rolagem. Primeiro, ele recupera a posição do elemento em relação à página de forma eficiente e sem usar uma função recursiva. Em seguida, obtém o xey do clique do mouse em relação à página e faz a subtração para obter a resposta que é a posição relativa ao elemento (o elemento pode ser uma imagem ou div por exemplo):

function getXY(evt) {
    var element = document.getElementById('elementId');  //replace elementId with your element's Id.
    var rect = element.getBoundingClientRect();
    var scrollTop = document.documentElement.scrollTop?
                    document.documentElement.scrollTop:document.body.scrollTop;
    var scrollLeft = document.documentElement.scrollLeft?                   
                    document.documentElement.scrollLeft:document.body.scrollLeft;
    var elementLeft = rect.left+scrollLeft;  
    var elementTop = rect.top+scrollTop;

        if (document.all){ //detects using IE   
            x = event.clientX+scrollLeft-elementLeft; //event not evt because of IE
            y = event.clientY+scrollTop-elementTop;
        }
        else{
            x = evt.pageX-elementLeft;
            y = evt.pageY-elementTop;
    }
Abdul Rahim Haddad
fonte
3

Se você tornar seu elemento pai "position: relative", será o "pai offset" para as coisas sobre as quais você está rastreando os eventos do mouse. Portanto, o jQuery "position ()" será relativo a isso.

Pontudo
fonte
Hey Pointy, obrigado pela sua resposta. Com base em sua redação, parece que o que você está sugerindo é diferente do que o jball sugeriu, mas não estou entendendo como, você pode elaborar? Obrigado novamente
Chris Dutrow
@DutrowLLC, Pointy está sugerindo mudar o estilo do elemento pai para ter "position:relative". A posição do jQuery reportaria suas posições em relação ao elemento pai, não à página. A redação da minha resposta é ruim, implica que ela está diretamente relacionada à solução de Pointy, mas é uma maneira de calcular o deslocamento quando você não pode ou não quer depender dos atributos de estilo do elemento pai.
jball
Portanto, se eu definir "position: relative", o jQuery reportará a posição relativa corretamente em todos os navegadores? A razão pela qual pergunto é porque a documentação do jQuery parecia sugerir que a única posição confiável do mouse em todos os navegadores era aquela em relação à própria janela do navegador.
Chris Dutrow 04/04/19
Uma caixa com "posição: relativa" fará a configuração "superior" e "esquerda" (etc.) para caixas filho em relação ao retângulo de conteúdo da caixa pai relativa.
Pointy
Posso estar fazendo algo errado, mas isso não parece funcionar no firefox. Eu tenho "posição: relativa;" set no pai, firefox relata event.pageX e event.clientX identicamente (em relação ao topo, à esquerda da página) e relata event.offsetX como indefinido
Chris Dutrow
2

Aqui está um que também fornece a porcentagem da posição do ponto, caso você precise. https://jsfiddle.net/Themezly/2etbhw01/

function ThzhotspotPosition(evt, el, hotspotsize, percent) {
  var left = el.offset().left;
  var top = el.offset().top;
  var hotspot = hotspotsize ? hotspotsize : 0;
  if (percent) {
    x = (evt.pageX - left - (hotspot / 2)) / el.outerWidth() * 100 + '%';
    y = (evt.pageY - top - (hotspot / 2)) / el.outerHeight() * 100 + '%';
  } else {
    x = (evt.pageX - left - (hotspot / 2));
    y = (evt.pageY - top - (hotspot / 2));
  }

  return {
    x: x,
    y: y
  };
}



$(function() {

  $('.box').click(function(e) {

    var hp = ThzhotspotPosition(e, $(this), 20, true); // true = percent | false or no attr = px

    var hotspot = $('<div class="hotspot">').css({
      left: hp.x,
      top: hp.y,
    });
    $(this).append(hotspot);
    $("span").text("X: " + hp.x + ", Y: " + hp.y);
  });


});
.box {
  width: 400px;
  height: 400px;
  background: #efefef;
  margin: 20px;
  padding: 20px;
  position: relative;
  top: 20px;
  left: 20px;
}

.hotspot {
  position: absolute;
  left: 0;
  top: 0;
  height: 20px;
  width: 20px;
  background: green;
  border-radius: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box">
  <p>Hotspot position is at: <span></span></p>
</div>

Benn
fonte
-1

Eu sugeriria isso:

e.pageX - this.getBoundingClientRect().left
Eduard
fonte