174

I was hoping to craft a control where a user could click inside a div, then drag the mouse, then let up on the mouse in order to indicate how long they want something to be. (This is for a calendar control, so the user will be indicating the length, in time, of a certain event)

It looks like the best way to do this would be to register a "mousedown" event on the parent div that in turn registers a "mousemove" event on the div until a "mouseup" event is triggered. The "mousedown" and "mouseup" events will define the start and end of the time range and as I follow "mousemove" events, I can dynamically change the size of the range so that the user can see what they are doing. I based this off of how events are created in google calendar.

The issue I'm having is that the jQuery event seems to only provide reliable mouse coordinate information in reference to the whole page. Is there any way to discern what the coordinates are in reference to the parent element?

Edit:

Heres a picture of what I'm trying to do: alt text

GEOCHET
  • 21,119
  • 15
  • 74
  • 98
Chris Dutrow
  • 48,402
  • 65
  • 188
  • 258

8 Answers8

322

One way is to use the jQuery offset method to translate the event.pageX and event.pageY coordinates from the event into a mouse position relative to the parent. Here's an example for future reference:

$("#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;
});
James Watkins
  • 4,806
  • 5
  • 32
  • 42
jball
  • 24,791
  • 9
  • 70
  • 92
  • 2
    Yes this is a great idea if the parent element's layout can't/shouldn't be changed! – Pointy Nov 22 '10 at 20:12
  • Great , i was looking for this. I have being tring to do something similar to remember the scroll bar position with the tiny scrollbar jquery plugin. – Gayan Hewa Mar 21 '12 at 05:06
  • I tried jquerys offsetX properties, but they displayed some wierd behaviour when moving over another element. your solution however worked! – jcfrei Jul 29 '12 at 23:07
  • 3
    Does this answer take into account padding/borders? – Lee Goddard Jun 20 '13 at 10:03
  • 10
    According to the docs, [offset](http://api.jquery.com/offset/) does not account "for borders, margins, or padding set on the body element." I haven't encountered a problem with my posted answer in use, but it might be good to check for those being set. – jball Jun 20 '13 at 16:00
  • 2
    This doesn't have to work. `offset()` is relative too, so if the parent is a child of other element, this will give wrong result. Use `position()` instead. – Flash Thunder Nov 10 '13 at 13:02
  • This doesn't work for something centered with `text-align:center` – Dex Jan 21 '14 at 04:36
  • @FlashThunder: `position()` always returns (0,0) for me. But the problem with `offset()` is that it can return floats, because of this: http://stackoverflow.com/a/4626568/1450294 – Michael Scheper Nov 13 '14 at 23:19
  • 1
    @FlashThunder offset() is not relative, at least not according to the docs. Also it should be standard practice to not have any margins or padding on the body element. – James Watkins Nov 08 '15 at 03:08
  • `relY` is returning `NaN`, I just want to know if a click occurred from within the element Y axis, 0 - 30 pixels from the very top of the element. But this approach does not work at all, since it returns `NaN`. – Solomon Closson Aug 03 '16 at 18:24
  • If the event is within an SVG, then you will need to use `getScreenCTM().inverse()`. See the wizardry at http://stackoverflow.com/a/10298843/93910 – Sanjay Manohar Jan 08 '17 at 23:18
18

To get the position of click relative to current clicked element
Use this code

$("#specialElement").click(function(e){
    var x = e.pageX - this.offsetLeft;
    var y = e.pageY - this.offsetTop;
}); 
Sajjad Ashraf
  • 3,754
  • 1
  • 34
  • 35
  • 2
    Your answer helped me BTW, the accepted answer did not. Thanks :) What I actually used is this, instead: `var y = e.pageY - $(this).offset().top;` But your answer led me on the right path. – Solomon Closson Aug 03 '16 at 18:33
12

I use this piece of code, its quite nice :)

    <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
  • 1,611
  • 6
  • 26
  • 45
10

@AbdulRahim answer is almost correct. But I suggest the function below as substitute (for futher reference):

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
  • 2,499
  • 6
  • 42
  • 68
8

This solution supports all major browsers including IE. It also takes care of scrolling. First, it retrieves the position of the element relative to the page efficiently, and without using a recursive function. Then it gets the x and y of the mouse click relative to the page and does the subtraction to get the answer which is the position relative to the element (the element can be an image or div for example):

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
  • 689
  • 1
  • 8
  • 14
4

Here is one that also gives you percent position of the point in case you need it. 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
  • 4,840
  • 8
  • 65
  • 106
3

If you make your parent element be "position: relative", then it will be the "offset parent" for the stuff you're tracking mouse events over. Thus the jQuery "position()" will be relative to that.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • Hey Pointy, thanks for your response. Based on your wording, it sounds like what you are suggesting is different than what jball suggested, but I'm not understanding how, can you elaborate? Thanks again – Chris Dutrow Dec 02 '10 at 17:38
  • @DutrowLLC, Pointy is suggesting changing the style of the parent element to have `"position:relative"`. The jQuery position would then report its positions relative to the parent element, not the page. The wording on my answer is poor, it implies that it is directly related to Pointy's solution, but it is a way of calculating the offset when you can't or don't want to depend on the style attributes of the parent element. – jball Dec 02 '10 at 17:43
  • So if I set "position:relative" jQuery will report the relatiove position correctly in all browsers? The reason I ask is because the jQuery documentation seemed to imply that the only dependable mouse position in all browsers was the one in relation to the browser window itself. – Chris Dutrow Dec 04 '10 at 19:01
  • A box with "position: relative" will make the "top" and "left" (etc.) setting for child boxes by relative to the relative parent box's content rectangle. – Pointy Dec 04 '10 at 21:58
  • I may be doing something wrong, but this does not appear to work in firefox. I have "position:relative;" set in the parent, firefox reports event.pageX and event.clientX identically (with respect to the top, left of the page) and reports event.offsetX as undefined – Chris Dutrow Dec 06 '10 at 17:47
  • I think there's a misunderstanding - what I was writing about was the returned results of the jQuery ".position()" function, which one might call for an element on the page. Have you checked the "clientX" and "clientY" values in the event? – Pointy Dec 06 '10 at 18:24
  • The question is about how to get the position of the mouse relative to the element, not how to get the position of an element relative to its parent, so the jQuery position() function isn't useful here. – James Watkins Nov 08 '15 at 02:54
  • @JamesWatkins *Is there any way to discern what the coordinates are in reference to the parent element?* The point of my answer that using relative positioning of the element in question (or an appropriate parent) makes it possible to interpret the mouse position and the element position on the same terms. – Pointy Nov 08 '15 at 14:31
  • Then you should be using offset(), not position(). And "position: relative" on the parent element is completely irrelevant, except maybe in very specific circumstances. I don't see any reason why this answer is helpful. – James Watkins Nov 08 '15 at 17:15
  • This about this mathematically. I have a cursor position (cpx, cpy) global to the page, and I want to find the position of said cursor within parent element located at (px, py) relative to its parent. So I do (cpx-px, cpy-py) but this is wrong, because the parent's parent may have a position. Offset() is the only solution here. – James Watkins Nov 08 '15 at 17:21
-1

I would suggest this:

e.pageX - this.getBoundingClientRect().left
trincot
  • 317,000
  • 35
  • 244
  • 286
Eduard
  • 1
  • 1