205

How can I retrieve the offset of a container relative to a parent with pure JS?

Abacus Lever
  • 330
  • 2
  • 8
matt
  • 42,713
  • 103
  • 264
  • 397

7 Answers7

279

element.offsetLeft and element.offsetTop give an element's position with respect to its offsetParent (which is the nearest parent element with a position of relative or absolute.)

temporary_user_name
  • 35,956
  • 47
  • 141
  • 220
jackwanders
  • 15,612
  • 3
  • 40
  • 40
111

in pure js just use offsetLeft and offsetTop properties.
Example fiddle: http://jsfiddle.net/WKZ8P/

var elm = document.querySelector('span');
console.log(elm.offsetLeft, elm.offsetTop);
p   { position:relative; left:10px; top:85px; border:1px solid blue; }
span{ position:relative; left:30px; top:35px; border:1px solid red; }
<p>
    <span>paragraph</span>
</p>
vsync
  • 118,978
  • 58
  • 307
  • 400
Fabrizio Calderan
  • 120,726
  • 26
  • 164
  • 177
  • Thank you. Is there also a way to get the offset to a parent.parent element? – matt Jul 24 '12 at 16:09
  • 3
    `p.offsetTop + p.parentNode.offsetTop` You could wrap this in a recursive function call much [like PPK proposed](http://www.quirksmode.org/js/findpos.html) a few years ago. You could alter the function to either pass the number of levels to go up or use a selector to mimic `$(selector).parents(parentSelector)`. I would prefer this solution because the zepto implementation only works on browsers supporting `getBoundingClientsRect` - which some mobiles don't. – Torsten Walter Jul 24 '12 at 21:21
  • I thought `getBoundingClientsRect` was widely supported... if anyone knows which mobiles do not support it I would be interested (can't find any support table for mobiles about it). – Aurelio Jul 01 '14 at 11:54
  • This is probably the most correct answer even though it's not marked. My personal solution was changing `$(this).offset().left` to `this.offsetLeft`. – adjwilli Aug 06 '14 at 15:10
  • This also fixes wrong `jQuery.position()` behavior inside of a 3d transformed parent, thank you so much! – rassoh Dec 03 '17 at 15:05
  • -1 , It works relative to "offsetParent"(https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent) which is NOT always the parent element, so this method won't work if the parent element is not positioned. – aderchox Jul 23 '21 at 14:02
7

I did it like this in Internet Explorer.

function getWindowRelativeOffset(parentWindow, elem) {
    var offset = {
        left : 0,
        top : 0
    };

    // relative to the target field's document
    offset.left = elem.getBoundingClientRect().left;
    offset.top = elem.getBoundingClientRect().top;

    // now we will calculate according to the current document, this current
    // document might be same as the document of target field or it may be
    // parent of the document of the target field
    var childWindow = elem.document.frames.window;
    while (childWindow != parentWindow) {
        offset.left = offset.left + childWindow.frameElement.getBoundingClientRect().left;
        offset.top = offset.top + childWindow.frameElement.getBoundingClientRect().top;
        childWindow = childWindow.parent;
    }

    return offset;
};

=================== you can call it like this

getWindowRelativeOffset(top, inputElement);

I focus on IE only as per my focus but similar things can be done for other browsers.

Salih Kavaf
  • 897
  • 9
  • 17
Imran Ansari
  • 139
  • 2
  • 1
3

Add the offset of the event to the parent element offset to get the absolute offset position of the event.

An example :

HTMLElement.addEventListener('mousedown',function(e){

    var offsetX = e.offsetX;
    var offsetY = e.offsetY;

    if( e.target != this ){ // 'this' is our HTMLElement

        offsetX = e.target.offsetLeft + e.offsetX;
        offsetY = e.target.offsetTop + e.offsetY;

    }
}

When the event target is not the element which the event was registered to, it adds the offset of the parent to the current event offset in order to calculate the "Absolute" offset value.

According to Mozilla Web API: "The HTMLElement.offsetLeft read-only property returns the number of pixels that the upper left corner of the current element is offset to the left within the HTMLElement.offsetParent node."

This mostly happens when you registered an event on a parent which is containing several more children, for example: a button with an inner icon or text span, an li element with inner spans. etc...

DeiDei
  • 10,205
  • 6
  • 55
  • 80
  • What about the parent of the parent? What about nested elements having scrollbars? What about previous elements with display:none? A true document offset position is almost impossible to calculate, yet the rendering engine can know it. Too bad simple properties like document position were never included in DOM. – David Spector Dec 07 '18 at 01:01
  • @DavidSpector word –  Dec 16 '18 at 09:19
3

I got another Solution. Subtract parent property value from child property value

$('child-div').offset().top - $('parent-div').offset().top;
Irshad Khan
  • 5,670
  • 2
  • 44
  • 39
1

Example

So, if we had a child element with an id of "child-element" and we wanted to get it's left/top position relative to a parent element, say a div that had a class of "item-parent", we'd use this code.

var position = $("#child-element").offsetRelative("div.item-parent");
alert('left: '+position.left+', top: '+position.top);

Plugin Finally, for the actual plugin (with a few notes explaining what's going on):

// offsetRelative (or, if you prefer, positionRelative)
(function($){
    $.fn.offsetRelative = function(top){
        var $this = $(this);
        var $parent = $this.offsetParent();
        var offset = $this.position();
        if(!top) return offset; // Didn't pass a 'top' element 
        else if($parent.get(0).tagName == "BODY") return offset; // Reached top of document
        else if($(top,$parent).length) return offset; // Parent element contains the 'top' element we want the offset to be relative to 
        else if($parent[0] == $(top)[0]) return offset; // Reached the 'top' element we want the offset to be relative to 
        else { // Get parent's relative offset
            var parent_offset = $parent.offsetRelative(top);
            offset.top += parent_offset.top;
            offset.left += parent_offset.left;
            return offset;
        }
    };

    $.fn.positionRelative = function(top){
        return $(this).offsetRelative(top);
    };
}(jQuery));

Note : You can Use this on mouseClick or mouseover Event

$(this).offsetRelative("div.item-parent");
Salih Kavaf
  • 897
  • 9
  • 17
Irshad Khan
  • 5,670
  • 2
  • 44
  • 39
0

Sure is easy with pure JS, just do this, work for fixed and animated HTML 5 panels too, i made and try this code and it works for any brower (include IE 8):

<script type="text/javascript">
    function fGetCSSProperty(s, e) {
        try { return s.currentStyle ? s.currentStyle[e] : window.getComputedStyle(s)[e]; }
        catch (x) { return null; } 
    }
    function fGetOffSetParent(s) {
        var a = s.offsetParent || document.body;

        while (a && a.tagName && a != document.body && fGetCSSProperty(a, 'position') == 'static')
            a = a.offsetParent;
        return a;
    }
    function GetPosition(s) {
        var b = fGetOffSetParent(s);

        return { Left: (b.offsetLeft + s.offsetLeft), Top: (b.offsetTop + s.offsetTop) };
    }    
</script>
Moises Conejo
  • 157
  • 1
  • 5
  • Did you test with all possible cases of parent nodes and element properties? Does this account for scrolling and nested scrolling? – David Spector Dec 07 '18 at 01:03