jQuery obtiene la posición del mouse dentro de un elemento

165

Esperaba crear un control en el que un usuario pudiera hacer clic dentro de un div, luego arrastrar el mouse y luego soltar el mouse para indicar cuánto tiempo quieren que sea algo. (Esto es para un control de calendario, por lo que el usuario indicará la duración, a tiempo, de un determinado evento)

Parece que la mejor manera de hacer esto sería registrar un evento "mousedown" en el div padre que a su vez registra un evento "mousemove" en el div hasta que se active un evento "mouseup". Los eventos "mousedown" y "mouseup" definirán el inicio y el final del rango de tiempo y, a medida que sigo los eventos de "mousemove", puedo cambiar dinámicamente el tamaño del rango para que el usuario pueda ver lo que está haciendo. Basé esto en cómo se crean los eventos en Google Calendar.

El problema que tengo es que el evento jQuery parece proporcionar solo información confiable de coordenadas del mouse en referencia a toda la página. ¿Hay alguna manera de discernir cuáles son las coordenadas en referencia al elemento padre?

Editar:

Aquí hay una foto de lo que estoy tratando de hacer: texto alternativo

Chris Dutrow
fuente

Respuestas:

305

Una forma es usar el offsetmétodo jQuery para traducir las coordenadas event.pageXy event.pageYdel evento a la posición del mouse en relación con el padre. Aquí hay un ejemplo para referencia 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
fuente
2
Sí, esta es una gran idea si el diseño del elemento padre no puede / no debe cambiarse.
Puntiagudo
2
¿Esta respuesta tiene en cuenta el relleno / bordes?
LeeGee
10
Según los documentos, el desplazamiento no tiene en cuenta "los bordes, los márgenes o el relleno del conjunto de elementos". No he encontrado un problema con mi respuesta publicada en uso, pero podría ser bueno verificar si están configuradas.
jball
2
Esto no tiene que funcionar. offset()también es relativo, por lo que si el padre es hijo de otro elemento, esto dará un resultado incorrecto. Usar en su position()lugar.
Flash Thunder
1
@FlashThunder offset () no es relativo, al menos no según los documentos. También debería ser una práctica estándar no tener márgenes o relleno en el elemento del cuerpo.
James Watkins el
18

Para obtener la posición del clic en relación con el elemento seleccionado actual
Use este código

$("#specialElement").click(function(e){
    var x = e.pageX - this.offsetLeft;
    var y = e.pageY - this.offsetTop;
}); 
Sajjad Ashraf
fuente
2
Su respuesta me ayudó, por cierto, la respuesta aceptada no. Gracias :) Lo que realmente usé es esto, en cambio: var y = e.pageY - $(this).offset().top;pero tu respuesta me llevó por el camino correcto.
Solomon Closson
11

Yo uso este código, es bastante agradable :)

    <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
fuente
9

La respuesta de @AbdulRahim es casi correcta. Pero sugiero la siguiente función como sustituto (para referencia adicional):

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
fuente
1
Mejora brillante, me salvó el día.
Bud Damyanov
casi, agregue margen al lienzo y estará fuera por el margen
Benn
Este es un gran fragmento de código, ¡muchas gracias!
Stefan
7

Esta solución es compatible con todos los principales navegadores, incluido IE. También se encarga del desplazamiento. Primero, recupera la posición del elemento en relación con la página de manera eficiente y sin utilizar una función recursiva. Luego obtiene la x e y del clic del mouse con relación a la página y hace la resta para obtener la respuesta, que es la posición relativa al elemento (el elemento puede ser una imagen o div, por ejemplo):

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
fuente
3

Si hace que su elemento padre sea "position: relative", será el "desplazamiento padre" para las cosas sobre las que rastrea los eventos del mouse. Por lo tanto, la "posición ()" de jQuery será relativa a eso.

Puntiagudo
fuente
Hola Pointy, gracias por tu respuesta. Según su redacción, parece que lo que sugiere es diferente de lo que sugirió jball, pero no entiendo cómo, ¿puede explicarlo? Gracias de nuevo
Chris Dutrow
@DutrowLLC, Pointy sugiere cambiar el estilo del elemento principal que debe tener "position:relative". La posición de jQuery informaría sus posiciones relativas al elemento padre, no a la página. La redacción de mi respuesta es deficiente, implica que está directamente relacionada con la solución de Pointy, pero es una forma de calcular el desplazamiento cuando no puede o no quiere depender de los atributos de estilo del elemento padre.
jball
Entonces, si configuro "position: relative", jQuery informará la posición de la relación correctamente en todos los navegadores? La razón por la que pregunto es porque la documentación de jQuery parecía implicar que la única posición confiable del mouse en todos los navegadores era la relacionada con la ventana del navegador.
Chris Dutrow
Un cuadro con "posición: relativo" establecerá la configuración "superior" e "izquierda" (etc.) para los cuadros secundarios en relación con el rectángulo de contenido del cuadro primario relativo.
Puntiagudo
Puede que esté haciendo algo mal, pero esto no parece funcionar en Firefox. Tengo "posición: relativa"; establecido en el padre, firefox informa event.pageX y event.clientX de forma idéntica (con respecto a la parte superior, izquierda de la página) e informa event.offsetX como indefinido
Chris Dutrow
2

Aquí hay uno que también le da el porcentaje de posición del punto en caso de que lo necesite. 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
fuente
-1

Sugeriría esto:

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