jQuery obtient la position de la souris dans un élément

165

J'espérais créer un contrôle où un utilisateur pourrait cliquer à l'intérieur d'un div, puis faire glisser la souris, puis relâcher la souris pour indiquer combien de temps il veut que quelque chose dure. (Ceci est pour un contrôle de calendrier, donc l'utilisateur indiquera la durée, dans le temps, d'un certain événement)

Il semble que la meilleure façon de faire cela serait d'enregistrer un événement "mousedown" sur le div parent qui à son tour enregistre un événement "mousemove" sur le div jusqu'à ce qu'un événement "mouseup" soit déclenché. Les événements "mousedown" et "mouseup" définiront le début et la fin de la plage horaire et, à mesure que je suivrai les événements "mousemove", je pourrai modifier dynamiquement la taille de la plage pour que l'utilisateur puisse voir ce qu'il fait. Je me suis basé sur la façon dont les événements sont créés dans le calendrier Google.

Le problème que j'ai est que l'événement jQuery semble ne fournir que des informations de coordonnées de souris fiables en référence à la page entière. Existe-t-il un moyen de discerner quelles sont les coordonnées en référence à l'élément parent?

Éditer:

Voici une image de ce que j'essaie de faire: texte alternatif

Chris Dutrow
la source

Réponses:

305

Une façon consiste à utiliser la offsetméthode jQuery pour traduire les coordonnées event.pageXet event.pageYde l'événement en une position de la souris par rapport au parent. Voici un exemple pour référence future:

$("#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
la source
2
Oui, c'est une excellente idée si la disposition de l'élément parent ne peut / ne doit pas être modifiée!
Pointy du
2
Cette réponse prend-elle en compte le rembourrage / les bordures?
LeeGee
10
Selon la documentation, le décalage ne tient pas compte «des bordures, des marges ou du remplissage définis sur l'élément de corps». Je n'ai pas rencontré de problème avec ma réponse publiée en cours d'utilisation, mais il peut être bon de vérifier ceux qui sont définis.
jball
2
Cela ne doit pas fonctionner. offset()est relative aussi, donc si le parent est un enfant d'un autre élément, cela donnera un mauvais résultat. Utilisez position()plutôt.
Flash Thunder
1
@FlashThunder offset () n'est pas relatif, du moins pas selon la documentation. Il devrait également être une pratique courante de ne pas avoir de marges ou de rembourrage sur l'élément de corps.
James Watkins
18

Pour obtenir la position du clic par rapport à l'élément cliqué actuel
Utilisez ce code

$("#specialElement").click(function(e){
    var x = e.pageX - this.offsetLeft;
    var y = e.pageY - this.offsetTop;
}); 
Sajjad Ashraf
la source
2
Votre réponse m'a aidé BTW, la réponse acceptée n'a pas. Merci :) Ce que j'ai réellement utilisé est ceci, à la place: var y = e.pageY - $(this).offset().top;Mais votre réponse m'a conduit sur la bonne voie.
Solomon Closson
11

J'utilise ce morceau de code, c'est assez sympa :)

    <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
la source
9

La réponse @AbdulRahim est presque correcte. Mais je suggère la fonction ci-dessous comme substitut (pour référence ultérieure):

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
la source
1
Brillante amélioration, m'a sauvé la journée.
Bud Damyanov
presque, ajoutez de la marge à la toile et vous êtes hors de la marge
Benn
C'est un bon morceau de code, merci beaucoup!
Stefan le
7

Cette solution prend en charge tous les principaux navigateurs, y compris IE. Il s'occupe également du défilement. Tout d'abord, il récupère efficacement la position de l'élément par rapport à la page, et sans utiliser de fonction récursive. Ensuite, il obtient les x et y du clic de souris par rapport à la page et fait la soustraction pour obtenir la réponse qui est la position par rapport à l'élément (l'élément peut être une image ou un div par exemple):

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
la source
3

Si vous faites de votre élément parent "position: relative", alors ce sera le "parent de décalage" pour les éléments sur lesquels vous suivez les événements de souris. Ainsi, la jQuery "position ()" sera relative à cela.

Pointu
la source
Hey Pointy, merci pour votre réponse. D'après votre libellé, il semble que ce que vous suggérez est différent de ce que jball a suggéré, mais je ne comprends pas comment, pouvez-vous élaborer? Merci encore
Chris Dutrow
@DutrowLLC, Pointy suggère de changer le style de l'élément parent à avoir "position:relative". La position jQuery rapporterait alors ses positions par rapport à l'élément parent, pas à la page. Le libellé de ma réponse est médiocre, cela implique qu'il est directement lié à la solution de Pointy, mais c'est un moyen de calculer le décalage lorsque vous ne pouvez pas ou ne voulez pas dépendre des attributs de style de l'élément parent.
jball
Donc si je mets "position: relative" jQuery rapportera correctement la position relative dans tous les navigateurs? La raison pour laquelle je pose la question est que la documentation de jQuery semblait impliquer que la seule position fiable de la souris dans tous les navigateurs était celle par rapport à la fenêtre du navigateur elle-même.
Chris Dutrow
Une boîte avec "position: relative" fera le réglage "haut" et "gauche" (etc.) pour les boîtes enfants par rapport au rectangle de contenu de la boîte parent relative.
Pointy
Je fais peut-être quelque chose de mal, mais cela ne semble pas fonctionner dans Firefox. J'ai "position: relative;" défini dans le parent, firefox signale event.pageX et event.clientX de la même manière (par rapport au haut à gauche de la page) et signale event.offsetX comme indéfini
Chris Dutrow
2

En voici un qui vous donne également la position en pourcentage du point au cas où vous en auriez besoin. 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
la source
-1

Je suggérerais ceci:

e.pageX - this.getBoundingClientRect().left
Eduard
la source