Get position of element relative to the page (HTML document) [duplicate] - javascript

I want to know how to get the X and Y position of HTML elements such as img and div in JavaScript.

The correct approach is to use element.getBoundingClientRect():
var rect = element.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);
Internet Explorer has supported this since as long as you are likely to care about and it was finally standardized in CSSOM Views. All other browsers adopted it a long time ago.
Some browsers also return height and width properties, though this is non-standard. If you're worried about older browser compatibility, check this answer's revisions for an optimised degrading implementation.
The values returned by element.getBoundingClientRect() are relative to the viewport. If you need it relative to another element, simply subtract one rectangle from the other:
var bodyRect = document.body.getBoundingClientRect(),
elemRect = element.getBoundingClientRect(),
offset = elemRect.top - bodyRect.top;
alert('Element is ' + offset + ' vertical pixels from <body>');

This function returns an element's position relative to the whole document (page):
function getOffset(el) {
const rect = el.getBoundingClientRect();
return {
left: rect.left + window.scrollX,
top: rect.top + window.scrollY
};
}
Using this we can get the X position:
getOffset(element).left
... or the Y position:
getOffset(element).top

The libraries go to some lengths to get accurate offsets for an element.
here's a simple function that does the job in every circumstances that I've tried.
function getOffset( el ) {
var _x = 0;
var _y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return { top: _y, left: _x };
}
var x = getOffset( document.getElementById('yourElId') ).left;

If you want it done only in javascript, here are some one liners using getBoundingClientRect()
window.scrollY + document.querySelector('#elementId').getBoundingClientRect().top // Y
window.scrollX + document.querySelector('#elementId').getBoundingClientRect().left // X
The first line will return offsetTop say Y relative to document.
The second line will return offsetLeft say X relative to document.
getBoundingClientRect() is a javascript function that returns the position of the element relative to viewport of window.

HTML elements on most browsers will have:-
offsetLeft
offsetTop
These specifiy the position of the element relative its nearest parent that has layout. This parent can often be accessed bif the offsetParent property.
IE and FF3 have
clientLeft
clientTop
These properties are less common, they specify an elements position with its parents client area (padded area is part of the client area but border and margin is not).

If page includes - at least- any "DIV", the function given by meouw throws the "Y" value beyond current page limits. In order to find the exact position, you need to handle both offsetParent's and parentNode's.
Try the code given below (it is checked for FF2):
var getAbsPosition = function(el){
var el2 = el;
var curtop = 0;
var curleft = 0;
if (document.getElementById || document.all) {
do {
curleft += el.offsetLeft-el.scrollLeft;
curtop += el.offsetTop-el.scrollTop;
el = el.offsetParent;
el2 = el2.parentNode;
while (el2 != el) {
curleft -= el2.scrollLeft;
curtop -= el2.scrollTop;
el2 = el2.parentNode;
}
} while (el.offsetParent);
} else if (document.layers) {
curtop += el.y;
curleft += el.x;
}
return [curtop, curleft];
};

You can add two properties to Element.prototype to get the top/left of any element.
Object.defineProperty( Element.prototype, 'documentOffsetTop', {
get: function () {
return this.offsetTop + ( this.offsetParent ? this.offsetParent.documentOffsetTop : 0 );
}
} );
Object.defineProperty( Element.prototype, 'documentOffsetLeft', {
get: function () {
return this.offsetLeft + ( this.offsetParent ? this.offsetParent.documentOffsetLeft : 0 );
}
} );
This is called like this:
var x = document.getElementById( 'myDiv' ).documentOffsetLeft;
Here's a demo comparing the results to jQuery's offset().top and .left: http://jsfiddle.net/ThinkingStiff/3G7EZ/

To retrieve the position relative to the page efficiently, and without using a recursive function: (includes IE also)
var element = document.getElementById('elementId'); //replace elementId with your element's Id.
var rect = element.getBoundingClientRect();
var elementLeft,elementTop; //x and y
var scrollTop = document.documentElement.scrollTop?
document.documentElement.scrollTop:document.body.scrollTop;
var scrollLeft = document.documentElement.scrollLeft?
document.documentElement.scrollLeft:document.body.scrollLeft;
elementTop = rect.top+scrollTop;
elementLeft = rect.left+scrollLeft;

How about something like this, by passing ID of the element and it will return the left or top, we can also combine them:
1) find left
function findLeft(element) {
var rec = document.getElementById(element).getBoundingClientRect();
return rec.left + window.scrollX;
} //call it like findLeft('#header');
2) find top
function findTop(element) {
var rec = document.getElementById(element).getBoundingClientRect();
return rec.top + window.scrollY;
} //call it like findTop('#header');
or 3) find left and top together
function findTopLeft(element) {
var rec = document.getElementById(element).getBoundingClientRect();
return {top: rec.top + window.scrollY, left: rec.left + window.scrollX};
} //call it like findTopLeft('#header');

Here's a modern 1-liner using vanilla JS to recursively iterate over element.offsetTop and element.offsetParent:
Function:
getTop = el => el.offsetTop + (el.offsetParent && getTop(el.offsetParent))
Usage:
const el = document.querySelector('#div_id');
const elTop = getTop(el)
Advantage:
Always returns the absolute vertical offset, regardless of the current scroll position.
Traditional syntax:
function getTop(el) {
return el.offsetTop + (el.offsetParent && getTop(el.offsetParent));
}

jQuery .offset() will get the current coordinates of the first element, or set the coordinates of every element, in the set of matched elements, relative to the document.

Update:
The recursion approach (in my old answer) creates many call stacks. We can use a while loop to avoid recursion in this case:
/**
*
* #param {HTMLElement} el
* #return {{top: number, left: number}}
*/
function getDocumentOffsetPosition(el) {
let top = 0, left = 0;
while (el !== null) {
top += el.offsetTop;
left += el.offsetLeft;
el = el.offsetParent;
}
return {top, left};
}
Old answer:
/**
*
* #param {HTMLElement} el
* #return {{top: number, left: number}}
*/
function getDocumentOffsetPosition(el) {
var position = {
top: el.offsetTop,
left: el.offsetLeft
};
if (el.offsetParent) {
var parentPosition = getDocumentOffsetPosition(el.offsetParent);
position.top += parentPosition.top;
position.left += parentPosition.left;
}
return position;
}
Thank ThinkingStiff for the answer, this is only another version.

You might be better served by using a JavaScript framework, that has functions to return such information (and so much more!) in a browser-independant fashion. Here are a few:
Prototype
jQuery
MooTools
YUI (yahoo)
With these frameworks, you could do something like:
$('id-of-img').top
to get the y-pixel coordinate of the image.

I've taken #meouw's answer, added in the clientLeft that allows for the border, and then created three versions:
getAbsoluteOffsetFromBody - similar to #meouw's, this gets the absolute position relative to the body or html element of the document (depending on quirks mode)
getAbsoluteOffsetFromGivenElement - returns the absolute position relative to the given element (relativeEl). Note that the given element must contain the element el, or this will behave the same as getAbsoluteOffsetFromBody. This is useful if you have two elements contained within another (known) element (optionally several nodes up the node tree) and want to make them the same position.
getAbsoluteOffsetFromRelative - returns the absolute position relative to the first parent element with position: relative. This is similar to getAbsoluteOffsetFromGivenElement, for the same reason but will only go as far as the first matching element.
getAbsoluteOffsetFromBody = function( el )
{ // finds the offset of el from the body or html element
var _x = 0;
var _y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
{
_x += el.offsetLeft - el.scrollLeft + el.clientLeft;
_y += el.offsetTop - el.scrollTop + el.clientTop;
el = el.offsetParent;
}
return { top: _y, left: _x };
}
getAbsoluteOffsetFromGivenElement = function( el, relativeEl )
{ // finds the offset of el from relativeEl
var _x = 0;
var _y = 0;
while( el && el != relativeEl && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
{
_x += el.offsetLeft - el.scrollLeft + el.clientLeft;
_y += el.offsetTop - el.scrollTop + el.clientTop;
el = el.offsetParent;
}
return { top: _y, left: _x };
}
getAbsoluteOffsetFromRelative = function( el )
{ // finds the offset of el from the first parent with position: relative
var _x = 0;
var _y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
{
_x += el.offsetLeft - el.scrollLeft + el.clientLeft;
_y += el.offsetTop - el.scrollTop + el.clientTop;
el = el.offsetParent;
if (el != null)
{
if (getComputedStyle !== 'undefined')
valString = getComputedStyle(el, null).getPropertyValue('position');
else
valString = el.currentStyle['position'];
if (valString === "relative")
el = null;
}
}
return { top: _y, left: _x };
}
If you are still having problems, particularly relating to scrolling, you could try looking at http://www.greywyvern.com/?post=331 - I noticed at least one piece of questionable code in getStyle which should be fine assuming browsers behave, but haven't tested the rest at all.

Difference between small and little
function getPosition( el ) {
var x = 0;
var y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
x += el.offsetLeft - el.scrollLeft;
y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return { top: y, left: x };
}
Look a example coordinates:
http://javascript.info/tutorial/coordinates

If you are using jQuery, this could be a simple solution:
<script>
var el = $("#element");
var position = el.position();
console.log( "left: " + position.left + ", top: " + position.top );
</script>

if using jQuery, the dimensions plugin is excellent and allows you specify exactly what you want.
e.g.
Relative position, absolute position, absolute position without padding, with padding...
It goes on, let's just say there is a lot you can do with it.
Plus the bonus of using jQuery is it's lightweight file size and easy use, you won't go back to JavaScript without it afterwards.

The cleanest approach I have found is a simplified version of the technique used by jQuery's offset. Similar to some of the other answers it starts with getBoundingClientRect; it then uses the window and the documentElement to adjust for scroll position as well as things like the margin on the body (often the default).
var rect = el.getBoundingClientRect();
var docEl = document.documentElement;
var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;
var els = document.getElementsByTagName("div");
var docEl = document.documentElement;
for (var i = 0; i < els.length; i++) {
var rect = els[i].getBoundingClientRect();
var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;
els[i].innerHTML = "<b>" + rectLeft + ", " + rectTop + "</b>";
}
div {
width: 100px;
height: 100px;
background-color: red;
border: 1px solid black;
}
#rel {
position: relative;
left: 10px;
top: 10px;
}
#abs {
position: absolute;
top: 250px;
left: 250px;
}
<div id="rel"></div>
<div id="abs"></div>
<div></div>

To get the total offset of an element, you could recursively sum up all parent offsets:
function getParentOffset(el): number {
if (el.offsetParent) {
return el.offsetParent.offsetTop + getParentOffset(el.offsetParent);
} else {
return 0;
}
}
with this utility function the total top offset of a dom element is:
el.offsetTop + getParentOffset(el);

This is the best code I've managed to create (works in iframes as well, unlike jQuery's offset()). Seems webkit has a bit of a different behavior.
Based on meouw's comment:
function getOffset( el ) {
var _x = 0;
var _y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
// chrome/safari
if ($.browser.webkit) {
el = el.parentNode;
} else {
// firefox/IE
el = el.offsetParent;
}
}
return { top: _y, left: _x };
}

While this is very likely to be lost at the bottom of so many answers, the top solutions here were not working for me.
As far as I could tell neither would any of the other answers have helped.
Situation:
In an HTML5 page I had a menu that was a nav element inside a header (not THE header but a header in another element).
I wanted the navigation to stick to the top once a user scrolled to it, but previous to this the header was absolute positioned (so I could have it overlay something else slightly).
The solutions above never triggered a change because .offsetTop was not going to change as this was an absolute positioned element. Additionally the .scrollTop property was simply the top of the top most element... that is to say 0 and always would be 0.
Any tests I performed utilizing these two (and same with getBoundingClientRect results) would not tell me if the top of the navigation bar ever scrolled to the top of the viewable page (again, as reported in console, they simply stayed the same numbers while scrolling occurred).
Solution
The solution for me was utilizing
window.visualViewport.pageTop
The value of the pageTop property reflects the viewable section of the screen, therefore allowing me to track where an element is in reference to the boundaries of the viewable area.
Probably unnecessary to say, anytime I am dealing with scrolling I expect to use this solution to programatically respond to movement of elements being scrolled.
Hope it helps someone else.
IMPORTANT NOTE: This appears to work in Chrome and Opera currently & definitely not in Firefox (6-2018)... until Firefox supports visualViewport I recommend NOT using this method, (and I hope they do soon... it makes a lot more sense than the rest).
UPDATE:
Just a note regarding this solution. While I still find what I discovered to be very valuable for situations in which "...programmatically respond to movement of elements being scrolled." is applicable. The better solution for the problem that I had was to use CSS to set position: sticky on the element. Using sticky you can have an element stay at the top without using javascript (NOTE: there are times this will not work as effectively as changing the element to fixed but for most uses the sticky approach will likely be superior)
UPDATE01:
So I realized that for a different page I had a requirement where I needed to detect the position of an element in a mildly complex scrolling setup (parallax plus elements that scroll past as part of a message).
I realized in that scenario that the following provided the value I utilized to determine when to do something:
let bodyElement = document.getElementsByTagName('body')[0];
let elementToTrack = bodyElement.querySelector('.trackme');
trackedObjPos = elementToTrack.getBoundingClientRect().top;
if(trackedObjPos > 264)
{
bodyElement.style.cssText = '';
}
Hope this answer is more widely useful now.

I did it like this so it was cross-compatible with old browsers.
// For really old browser's or incompatible ones
function getOffsetSum(elem) {
var top = 0,
left = 0,
bottom = 0,
right = 0
var width = elem.offsetWidth;
var height = elem.offsetHeight;
while (elem) {
top += elem.offsetTop;
left += elem.offsetLeft;
elem = elem.offsetParent;
}
right = left + width;
bottom = top + height;
return {
top: top,
left: left,
bottom: bottom,
right: right,
}
}
function getOffsetRect(elem) {
var box = elem.getBoundingClientRect();
var body = document.body;
var docElem = document.documentElement;
var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;
var clientTop = docElem.clientTop;
var clientLeft = docElem.clientLeft;
var top = box.top + scrollTop - clientTop;
var left = box.left + scrollLeft - clientLeft;
var bottom = top + (box.bottom - box.top);
var right = left + (box.right - box.left);
return {
top: Math.round(top),
left: Math.round(left),
bottom: Math.round(bottom),
right: Math.round(right),
}
}
function getOffset(elem) {
if (elem) {
if (elem.getBoundingClientRect) {
return getOffsetRect(elem);
} else { // old browser
return getOffsetSum(elem);
}
} else
return null;
}
More about coordinates in JavaScript here: http://javascript.info/tutorial/coordinates

HTML program to show (x, y) of an
element by dragging mouse over it you just copied it and use it on your own
<!DOCTYPE html>
<html>
<head>
<title>
position of an element
</title>
<!-- scropt to get position -->
<script type = "text/javascript">
function getPositionXY(element) {
var rect = element.getBoundingClientRect();
document.getElementById('text').innerHTML
= 'X: ' + rect.x + '<br>' + 'Y: ' + rect.y;
}
</script>
</head>
<body>
<p>Move the mouse over the text</p>
<div onmouseover = "getPositionXY(this)">
Position:
<p id = 'text'></p>
</div>
</body>
</html>

i could just like element.offsetLeft or element.offsetTop. Example :
document.getElementById('profileImg').offsetLeft

I successfully used Andy E's solution to position a bootstrap 2 modal depending on what link in a table row a user clicks on. The page is a Tapestry 5 page and javascript below is imported in the java page class.
javascript:
function setLinkPosition(clientId){
var bodyRect = document.body.getBoundingClientRect(),
elemRect = clientId.getBoundingClientRect(),
offset = elemRect.top - bodyRect.top;
offset = offset + 20;
$('#serviceLineModal').css("top", offset);
}
My modal code:
<div id="serviceLineModal" class="modal hide fade add-absolute-position" data-backdrop="static"
tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="top:50%;">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
<h3 id="myModalLabel">Modal header</h3>
</div>
<div class="modal-body">
<t:zone t:id="modalZone" id="modalZone">
<p>You selected service line number: ${serviceLineNumberSelected}</p>
</t:zone>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
<!-- <button class="btn btn-primary">Save changes</button> -->
</div>
The link in the loop:
<t:loop source="servicesToDisplay" value="service" encoder="encoder">
<tr style="border-right: 1px solid black;">
<td style="white-space:nowrap;" class="add-padding-left-and-right no-border">
<a t:type="eventLink" t:event="serviceLineNumberSelected" t:context="service.serviceLineNumber"
t:zone="pageZone" t:clientId="modalLink${service.serviceLineNumber}"
onmouseover="setLinkPosition(this);">
<i class="icon-chevron-down"></i> <!-- ${service.serviceLineNumber} -->
</a>
</td>
And the java code in the page class:
void onServiceLineNumberSelected(String number){
checkForNullSession();
serviceLineNumberSelected = number;
addOpenServiceLineDialogCommand();
ajaxResponseRenderer.addRender(modalZone);
}
protected void addOpenServiceLineDialogCommand() {
ajaxResponseRenderer.addCallback(new JavaScriptCallback() {
#Override
public void run(JavaScriptSupport javascriptSupport) {
javascriptSupport.addScript("$('#serviceLineModal').modal('show');");
}
});
}
Hope this helps someone, this post helped out.

After much research and testing this seems to work
function getPosition(e) {
var isNotFirefox = (navigator.userAgent.toLowerCase().indexOf('firefox') == -1);
var x = 0, y = 0;
while (e) {
x += e.offsetLeft - e.scrollLeft + (isNotFirefox ? e.clientLeft : 0);
y += e.offsetTop - e.scrollTop + (isNotFirefox ? e.clientTop : 0);
e = e.offsetParent;
}
return { x: x + window.scrollX, y: y + window.scrollY };
}
see http://jsbin.com/xuvovalifo/edit?html,js,output

Just thought I'd throw this out there as well.
I haven't been able to test it in older browsers, but it works in the latest of the top 3. :)
Element.prototype.getOffsetTop = function() {
return ( this.parentElement )? this.offsetTop + this.parentElement.getOffsetTop(): this.offsetTop;
};
Element.prototype.getOffsetLeft = function() {
return ( this.parentElement )? this.offsetLeft + this.parentElement.getOffsetLeft(): this.offsetLeft;
};
Element.prototype.getOffset = function() {
return {'left':this.getOffsetLeft(),'top':this.getOffsetTop()};
};

This is easy as two lines in JS :
var elem = document.getElementById("id");
alert(elem.getBoundingClientRect());

Since different browsers are rendering border, padding, margin and etc in different way. I wrote a little function to retrieve top and left positions of specific element in every root element that you want in precise dimension:
function getTop(root, offset) {
var rootRect = root.getBoundingClientRect();
var offsetRect = offset.getBoundingClientRect();
return offsetRect.top - rootRect.top;
}
For retrieve left position you must return:
return offsetRect.left - rootRect.left;

Get position of div in respect to left and Top
var elm = $('#div_id'); //get the div
var posY_top = elm.offset().top; //get the position from top
var posX_left = elm.offset().left; //get the position from left

Related

Need a javascript code that will get the Y height of an object to use as a variable for scrolling [duplicate]

I want to know how to get the X and Y position of HTML elements such as img and div in JavaScript.
The correct approach is to use element.getBoundingClientRect():
var rect = element.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);
Internet Explorer has supported this since as long as you are likely to care about and it was finally standardized in CSSOM Views. All other browsers adopted it a long time ago.
Some browsers also return height and width properties, though this is non-standard. If you're worried about older browser compatibility, check this answer's revisions for an optimised degrading implementation.
The values returned by element.getBoundingClientRect() are relative to the viewport. If you need it relative to another element, simply subtract one rectangle from the other:
var bodyRect = document.body.getBoundingClientRect(),
elemRect = element.getBoundingClientRect(),
offset = elemRect.top - bodyRect.top;
alert('Element is ' + offset + ' vertical pixels from <body>');
This function returns an element's position relative to the whole document (page):
function getOffset(el) {
const rect = el.getBoundingClientRect();
return {
left: rect.left + window.scrollX,
top: rect.top + window.scrollY
};
}
Using this we can get the X position:
getOffset(element).left
... or the Y position:
getOffset(element).top
The libraries go to some lengths to get accurate offsets for an element.
here's a simple function that does the job in every circumstances that I've tried.
function getOffset( el ) {
var _x = 0;
var _y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return { top: _y, left: _x };
}
var x = getOffset( document.getElementById('yourElId') ).left;
If you want it done only in javascript, here are some one liners using getBoundingClientRect()
window.scrollY + document.querySelector('#elementId').getBoundingClientRect().top // Y
window.scrollX + document.querySelector('#elementId').getBoundingClientRect().left // X
The first line will return offsetTop say Y relative to document.
The second line will return offsetLeft say X relative to document.
getBoundingClientRect() is a javascript function that returns the position of the element relative to viewport of window.
HTML elements on most browsers will have:-
offsetLeft
offsetTop
These specifiy the position of the element relative its nearest parent that has layout. This parent can often be accessed bif the offsetParent property.
IE and FF3 have
clientLeft
clientTop
These properties are less common, they specify an elements position with its parents client area (padded area is part of the client area but border and margin is not).
If page includes - at least- any "DIV", the function given by meouw throws the "Y" value beyond current page limits. In order to find the exact position, you need to handle both offsetParent's and parentNode's.
Try the code given below (it is checked for FF2):
var getAbsPosition = function(el){
var el2 = el;
var curtop = 0;
var curleft = 0;
if (document.getElementById || document.all) {
do {
curleft += el.offsetLeft-el.scrollLeft;
curtop += el.offsetTop-el.scrollTop;
el = el.offsetParent;
el2 = el2.parentNode;
while (el2 != el) {
curleft -= el2.scrollLeft;
curtop -= el2.scrollTop;
el2 = el2.parentNode;
}
} while (el.offsetParent);
} else if (document.layers) {
curtop += el.y;
curleft += el.x;
}
return [curtop, curleft];
};
You can add two properties to Element.prototype to get the top/left of any element.
Object.defineProperty( Element.prototype, 'documentOffsetTop', {
get: function () {
return this.offsetTop + ( this.offsetParent ? this.offsetParent.documentOffsetTop : 0 );
}
} );
Object.defineProperty( Element.prototype, 'documentOffsetLeft', {
get: function () {
return this.offsetLeft + ( this.offsetParent ? this.offsetParent.documentOffsetLeft : 0 );
}
} );
This is called like this:
var x = document.getElementById( 'myDiv' ).documentOffsetLeft;
Here's a demo comparing the results to jQuery's offset().top and .left: http://jsfiddle.net/ThinkingStiff/3G7EZ/
To retrieve the position relative to the page efficiently, and without using a recursive function: (includes IE also)
var element = document.getElementById('elementId'); //replace elementId with your element's Id.
var rect = element.getBoundingClientRect();
var elementLeft,elementTop; //x and y
var scrollTop = document.documentElement.scrollTop?
document.documentElement.scrollTop:document.body.scrollTop;
var scrollLeft = document.documentElement.scrollLeft?
document.documentElement.scrollLeft:document.body.scrollLeft;
elementTop = rect.top+scrollTop;
elementLeft = rect.left+scrollLeft;
How about something like this, by passing ID of the element and it will return the left or top, we can also combine them:
1) find left
function findLeft(element) {
var rec = document.getElementById(element).getBoundingClientRect();
return rec.left + window.scrollX;
} //call it like findLeft('#header');
2) find top
function findTop(element) {
var rec = document.getElementById(element).getBoundingClientRect();
return rec.top + window.scrollY;
} //call it like findTop('#header');
or 3) find left and top together
function findTopLeft(element) {
var rec = document.getElementById(element).getBoundingClientRect();
return {top: rec.top + window.scrollY, left: rec.left + window.scrollX};
} //call it like findTopLeft('#header');
Here's a modern 1-liner using vanilla JS to recursively iterate over element.offsetTop and element.offsetParent:
Function:
getTop = el => el.offsetTop + (el.offsetParent && getTop(el.offsetParent))
Usage:
const el = document.querySelector('#div_id');
const elTop = getTop(el)
Advantage:
Always returns the absolute vertical offset, regardless of the current scroll position.
Traditional syntax:
function getTop(el) {
return el.offsetTop + (el.offsetParent && getTop(el.offsetParent));
}
jQuery .offset() will get the current coordinates of the first element, or set the coordinates of every element, in the set of matched elements, relative to the document.
Update:
The recursion approach (in my old answer) creates many call stacks. We can use a while loop to avoid recursion in this case:
/**
*
* #param {HTMLElement} el
* #return {{top: number, left: number}}
*/
function getDocumentOffsetPosition(el) {
let top = 0, left = 0;
while (el !== null) {
top += el.offsetTop;
left += el.offsetLeft;
el = el.offsetParent;
}
return {top, left};
}
Old answer:
/**
*
* #param {HTMLElement} el
* #return {{top: number, left: number}}
*/
function getDocumentOffsetPosition(el) {
var position = {
top: el.offsetTop,
left: el.offsetLeft
};
if (el.offsetParent) {
var parentPosition = getDocumentOffsetPosition(el.offsetParent);
position.top += parentPosition.top;
position.left += parentPosition.left;
}
return position;
}
Thank ThinkingStiff for the answer, this is only another version.
You might be better served by using a JavaScript framework, that has functions to return such information (and so much more!) in a browser-independant fashion. Here are a few:
Prototype
jQuery
MooTools
YUI (yahoo)
With these frameworks, you could do something like:
$('id-of-img').top
to get the y-pixel coordinate of the image.
I've taken #meouw's answer, added in the clientLeft that allows for the border, and then created three versions:
getAbsoluteOffsetFromBody - similar to #meouw's, this gets the absolute position relative to the body or html element of the document (depending on quirks mode)
getAbsoluteOffsetFromGivenElement - returns the absolute position relative to the given element (relativeEl). Note that the given element must contain the element el, or this will behave the same as getAbsoluteOffsetFromBody. This is useful if you have two elements contained within another (known) element (optionally several nodes up the node tree) and want to make them the same position.
getAbsoluteOffsetFromRelative - returns the absolute position relative to the first parent element with position: relative. This is similar to getAbsoluteOffsetFromGivenElement, for the same reason but will only go as far as the first matching element.
getAbsoluteOffsetFromBody = function( el )
{ // finds the offset of el from the body or html element
var _x = 0;
var _y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
{
_x += el.offsetLeft - el.scrollLeft + el.clientLeft;
_y += el.offsetTop - el.scrollTop + el.clientTop;
el = el.offsetParent;
}
return { top: _y, left: _x };
}
getAbsoluteOffsetFromGivenElement = function( el, relativeEl )
{ // finds the offset of el from relativeEl
var _x = 0;
var _y = 0;
while( el && el != relativeEl && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
{
_x += el.offsetLeft - el.scrollLeft + el.clientLeft;
_y += el.offsetTop - el.scrollTop + el.clientTop;
el = el.offsetParent;
}
return { top: _y, left: _x };
}
getAbsoluteOffsetFromRelative = function( el )
{ // finds the offset of el from the first parent with position: relative
var _x = 0;
var _y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
{
_x += el.offsetLeft - el.scrollLeft + el.clientLeft;
_y += el.offsetTop - el.scrollTop + el.clientTop;
el = el.offsetParent;
if (el != null)
{
if (getComputedStyle !== 'undefined')
valString = getComputedStyle(el, null).getPropertyValue('position');
else
valString = el.currentStyle['position'];
if (valString === "relative")
el = null;
}
}
return { top: _y, left: _x };
}
If you are still having problems, particularly relating to scrolling, you could try looking at http://www.greywyvern.com/?post=331 - I noticed at least one piece of questionable code in getStyle which should be fine assuming browsers behave, but haven't tested the rest at all.
Difference between small and little
function getPosition( el ) {
var x = 0;
var y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
x += el.offsetLeft - el.scrollLeft;
y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return { top: y, left: x };
}
Look a example coordinates:
http://javascript.info/tutorial/coordinates
If you are using jQuery, this could be a simple solution:
<script>
var el = $("#element");
var position = el.position();
console.log( "left: " + position.left + ", top: " + position.top );
</script>
if using jQuery, the dimensions plugin is excellent and allows you specify exactly what you want.
e.g.
Relative position, absolute position, absolute position without padding, with padding...
It goes on, let's just say there is a lot you can do with it.
Plus the bonus of using jQuery is it's lightweight file size and easy use, you won't go back to JavaScript without it afterwards.
The cleanest approach I have found is a simplified version of the technique used by jQuery's offset. Similar to some of the other answers it starts with getBoundingClientRect; it then uses the window and the documentElement to adjust for scroll position as well as things like the margin on the body (often the default).
var rect = el.getBoundingClientRect();
var docEl = document.documentElement;
var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;
var els = document.getElementsByTagName("div");
var docEl = document.documentElement;
for (var i = 0; i < els.length; i++) {
var rect = els[i].getBoundingClientRect();
var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;
els[i].innerHTML = "<b>" + rectLeft + ", " + rectTop + "</b>";
}
div {
width: 100px;
height: 100px;
background-color: red;
border: 1px solid black;
}
#rel {
position: relative;
left: 10px;
top: 10px;
}
#abs {
position: absolute;
top: 250px;
left: 250px;
}
<div id="rel"></div>
<div id="abs"></div>
<div></div>
To get the total offset of an element, you could recursively sum up all parent offsets:
function getParentOffset(el): number {
if (el.offsetParent) {
return el.offsetParent.offsetTop + getParentOffset(el.offsetParent);
} else {
return 0;
}
}
with this utility function the total top offset of a dom element is:
el.offsetTop + getParentOffset(el);
This is the best code I've managed to create (works in iframes as well, unlike jQuery's offset()). Seems webkit has a bit of a different behavior.
Based on meouw's comment:
function getOffset( el ) {
var _x = 0;
var _y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
// chrome/safari
if ($.browser.webkit) {
el = el.parentNode;
} else {
// firefox/IE
el = el.offsetParent;
}
}
return { top: _y, left: _x };
}
While this is very likely to be lost at the bottom of so many answers, the top solutions here were not working for me.
As far as I could tell neither would any of the other answers have helped.
Situation:
In an HTML5 page I had a menu that was a nav element inside a header (not THE header but a header in another element).
I wanted the navigation to stick to the top once a user scrolled to it, but previous to this the header was absolute positioned (so I could have it overlay something else slightly).
The solutions above never triggered a change because .offsetTop was not going to change as this was an absolute positioned element. Additionally the .scrollTop property was simply the top of the top most element... that is to say 0 and always would be 0.
Any tests I performed utilizing these two (and same with getBoundingClientRect results) would not tell me if the top of the navigation bar ever scrolled to the top of the viewable page (again, as reported in console, they simply stayed the same numbers while scrolling occurred).
Solution
The solution for me was utilizing
window.visualViewport.pageTop
The value of the pageTop property reflects the viewable section of the screen, therefore allowing me to track where an element is in reference to the boundaries of the viewable area.
Probably unnecessary to say, anytime I am dealing with scrolling I expect to use this solution to programatically respond to movement of elements being scrolled.
Hope it helps someone else.
IMPORTANT NOTE: This appears to work in Chrome and Opera currently & definitely not in Firefox (6-2018)... until Firefox supports visualViewport I recommend NOT using this method, (and I hope they do soon... it makes a lot more sense than the rest).
UPDATE:
Just a note regarding this solution. While I still find what I discovered to be very valuable for situations in which "...programmatically respond to movement of elements being scrolled." is applicable. The better solution for the problem that I had was to use CSS to set position: sticky on the element. Using sticky you can have an element stay at the top without using javascript (NOTE: there are times this will not work as effectively as changing the element to fixed but for most uses the sticky approach will likely be superior)
UPDATE01:
So I realized that for a different page I had a requirement where I needed to detect the position of an element in a mildly complex scrolling setup (parallax plus elements that scroll past as part of a message).
I realized in that scenario that the following provided the value I utilized to determine when to do something:
let bodyElement = document.getElementsByTagName('body')[0];
let elementToTrack = bodyElement.querySelector('.trackme');
trackedObjPos = elementToTrack.getBoundingClientRect().top;
if(trackedObjPos > 264)
{
bodyElement.style.cssText = '';
}
Hope this answer is more widely useful now.
I did it like this so it was cross-compatible with old browsers.
// For really old browser's or incompatible ones
function getOffsetSum(elem) {
var top = 0,
left = 0,
bottom = 0,
right = 0
var width = elem.offsetWidth;
var height = elem.offsetHeight;
while (elem) {
top += elem.offsetTop;
left += elem.offsetLeft;
elem = elem.offsetParent;
}
right = left + width;
bottom = top + height;
return {
top: top,
left: left,
bottom: bottom,
right: right,
}
}
function getOffsetRect(elem) {
var box = elem.getBoundingClientRect();
var body = document.body;
var docElem = document.documentElement;
var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;
var clientTop = docElem.clientTop;
var clientLeft = docElem.clientLeft;
var top = box.top + scrollTop - clientTop;
var left = box.left + scrollLeft - clientLeft;
var bottom = top + (box.bottom - box.top);
var right = left + (box.right - box.left);
return {
top: Math.round(top),
left: Math.round(left),
bottom: Math.round(bottom),
right: Math.round(right),
}
}
function getOffset(elem) {
if (elem) {
if (elem.getBoundingClientRect) {
return getOffsetRect(elem);
} else { // old browser
return getOffsetSum(elem);
}
} else
return null;
}
More about coordinates in JavaScript here: http://javascript.info/tutorial/coordinates
HTML program to show (x, y) of an
element by dragging mouse over it you just copied it and use it on your own
<!DOCTYPE html>
<html>
<head>
<title>
position of an element
</title>
<!-- scropt to get position -->
<script type = "text/javascript">
function getPositionXY(element) {
var rect = element.getBoundingClientRect();
document.getElementById('text').innerHTML
= 'X: ' + rect.x + '<br>' + 'Y: ' + rect.y;
}
</script>
</head>
<body>
<p>Move the mouse over the text</p>
<div onmouseover = "getPositionXY(this)">
Position:
<p id = 'text'></p>
</div>
</body>
</html>
i could just like element.offsetLeft or element.offsetTop. Example :
document.getElementById('profileImg').offsetLeft
I successfully used Andy E's solution to position a bootstrap 2 modal depending on what link in a table row a user clicks on. The page is a Tapestry 5 page and javascript below is imported in the java page class.
javascript:
function setLinkPosition(clientId){
var bodyRect = document.body.getBoundingClientRect(),
elemRect = clientId.getBoundingClientRect(),
offset = elemRect.top - bodyRect.top;
offset = offset + 20;
$('#serviceLineModal').css("top", offset);
}
My modal code:
<div id="serviceLineModal" class="modal hide fade add-absolute-position" data-backdrop="static"
tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="top:50%;">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
<h3 id="myModalLabel">Modal header</h3>
</div>
<div class="modal-body">
<t:zone t:id="modalZone" id="modalZone">
<p>You selected service line number: ${serviceLineNumberSelected}</p>
</t:zone>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
<!-- <button class="btn btn-primary">Save changes</button> -->
</div>
The link in the loop:
<t:loop source="servicesToDisplay" value="service" encoder="encoder">
<tr style="border-right: 1px solid black;">
<td style="white-space:nowrap;" class="add-padding-left-and-right no-border">
<a t:type="eventLink" t:event="serviceLineNumberSelected" t:context="service.serviceLineNumber"
t:zone="pageZone" t:clientId="modalLink${service.serviceLineNumber}"
onmouseover="setLinkPosition(this);">
<i class="icon-chevron-down"></i> <!-- ${service.serviceLineNumber} -->
</a>
</td>
And the java code in the page class:
void onServiceLineNumberSelected(String number){
checkForNullSession();
serviceLineNumberSelected = number;
addOpenServiceLineDialogCommand();
ajaxResponseRenderer.addRender(modalZone);
}
protected void addOpenServiceLineDialogCommand() {
ajaxResponseRenderer.addCallback(new JavaScriptCallback() {
#Override
public void run(JavaScriptSupport javascriptSupport) {
javascriptSupport.addScript("$('#serviceLineModal').modal('show');");
}
});
}
Hope this helps someone, this post helped out.
After much research and testing this seems to work
function getPosition(e) {
var isNotFirefox = (navigator.userAgent.toLowerCase().indexOf('firefox') == -1);
var x = 0, y = 0;
while (e) {
x += e.offsetLeft - e.scrollLeft + (isNotFirefox ? e.clientLeft : 0);
y += e.offsetTop - e.scrollTop + (isNotFirefox ? e.clientTop : 0);
e = e.offsetParent;
}
return { x: x + window.scrollX, y: y + window.scrollY };
}
see http://jsbin.com/xuvovalifo/edit?html,js,output
Just thought I'd throw this out there as well.
I haven't been able to test it in older browsers, but it works in the latest of the top 3. :)
Element.prototype.getOffsetTop = function() {
return ( this.parentElement )? this.offsetTop + this.parentElement.getOffsetTop(): this.offsetTop;
};
Element.prototype.getOffsetLeft = function() {
return ( this.parentElement )? this.offsetLeft + this.parentElement.getOffsetLeft(): this.offsetLeft;
};
Element.prototype.getOffset = function() {
return {'left':this.getOffsetLeft(),'top':this.getOffsetTop()};
};
This is easy as two lines in JS :
var elem = document.getElementById("id");
alert(elem.getBoundingClientRect());
Since different browsers are rendering border, padding, margin and etc in different way. I wrote a little function to retrieve top and left positions of specific element in every root element that you want in precise dimension:
function getTop(root, offset) {
var rootRect = root.getBoundingClientRect();
var offsetRect = offset.getBoundingClientRect();
return offsetRect.top - rootRect.top;
}
For retrieve left position you must return:
return offsetRect.left - rootRect.left;
Get position of div in respect to left and Top
var elm = $('#div_id'); //get the div
var posY_top = elm.offset().top; //get the position from top
var posX_left = elm.offset().left; //get the position from left

position of element issue in IE?

I am using the following javascript function to get the absoulte position of an element
function findPos(obj) {
var curleft = 0;
if (obj.offsetParent) {
while (1) {
curleft += obj.offsetLeft;
if (!obj.offsetParent) {
break;
}
obj = obj.offsetParent;
}
}
}
The function is working fine in all browsers except IE .In IE also it works fine in full screen but when the browser is resized then this method returns incorrect offset.
Could you please help me out on this
I would suggest you to switch from using offsetParent/Top/Left to utilizing getBoundingClientRect() function (slightly enhanced). It should be more performant, because you don't have to loop through DOM and it should return correct results across browsers.
Your findPos function would look like this:
function findPos(obj) {
var gbcr = obj.getBoundingClientRect(),
dE = document.documentElement,
b = document.body,
scrollY = window.pageYOffset || dE.scrollTop || b.scrollTop, /* 1 */
scrollX = window.pageXOffset || dE.scrollLeft || b.scrollLeft, /* 1 */
top = gbcr.top + scrollY - dE.clientTop, /* 2 */
left = gbcr.left + scrollX - dE.clientLeft; /* 2 */
return { top: top, left: left };
}
getBoundingClientRect() doesn't take scroll into account, so you have to compensate that.
It is possible to set border on <html>, so without substracting it's value, the function could return incorrect element position relative to document.

Javascript: Let user select an HTML element like Firebug?

I want to write a browser (Chrome/FF) extension that needs to select an element on a web page. I would like it to behave like Firebug's element inspector does. You click the inspect arrow and you can then hover/highlight elements. When you click on the element you want, the element is inspected. I'm just interested in the code to allow a user to select an element - not in actually inspecting it or anything similar.
Because I'm writing an extension, it might be nice if you could provide non-jQuery/Prototype/etc.. code so I don't have to distribute that.
I have recently required such a feature for a project I was working on, turned out that I had to use for sides to create a box because otherwise the event.target when you move the mouse would end up being the selector, and if I were to use z-index: -1 it would be a bit fishy when you have a lot of elements that overlap...etc.
Here is a version that I have converted from my project for your benefit, it involves jQuery but it is extremely simple to convert to vanilla as only the mousemove & css methods from jQuery are used.
Step by step instructions.
First create the 5 HTMLElements that are required.
<div id="selector">
<div id="selector-top"></div>
<div id="selector-left"></div>
<div id="selector-right"></div>
<div id="selector-bottom"></div>
</div>
Secondly create a mousemove event on the document (or your container)
$(document).mousemove(function(event) { ... });
Then inside the mousemove we will do some basic checking to prevent selecting the HTML, BODY, selector
var id = event.target.id, tagName = event.target.tagName;
if(id.indexOf('selector') !== -1 || tagName === 'BODY' || tagName === 'HTML') {
return;
}
Then we need to create a object to store our elements like so.
var elements = {
top: $('#selector-top'),
left: $('#selector-left'),
right: $('#selector-right'),
bottom: $('#selector-bottom')
};
After that we store some variables that hold some information about the target element like so.
var $target = event.target;
targetOffset = $target.getBoundingClientRect(),
targetHeight = targetOffset.height,
targetWidth = targetOffset.width;
Then all we do is calculate the position & height for all 4 sides of the selector like so.
elements.top.css({
left: (targetOffset.left - 4),
top: (targetOffset.top - 4),
width: (targetWidth + 5)
});
elements.bottom.css({
top: (targetOffset.top + targetHeight + 1),
left: (targetOffset.left - 3),
width: (targetWidth + 4)
});
elements.left.css({
left: (targetOffset.left - 5),
top: (targetOffset.top - 4),
height: (targetHeight + 8)
});
elements.right.css({
left: (targetOffset.left + targetWidth + 1),
top: (targetOffset.top - 4),
height: (targetHeight + 8)
});
All of the +aFewPixels is just a little optimization so that there is like 2px gap in between the selector and the target.
For the CSS this is what I have come up with.
#selector-top, #selector-bottom {
background: blue;
height:3px;
position: fixed;
transition:all 300ms ease;
}
#selector-left, #selector-right {
background: blue;
width:3px;
position: fixed;
transition:all 300ms ease;
}
The transition gives the selector a very nice sliding effect.
Try out a demo http://jsfiddle.net/rFc8E/9/
Note: This also works for transform: scale(2); eg. when a element is scaled in size.
Edit: I've just updated this, I noticed that the elements object was inside the event handler, I've moved it outside in the demo, this is quite an important performance improvement because now, the elements object is only created once instead of Hundreds of Thousands if not millions of times inside the mousemove event.
I wrote an implementation of this using jQuery as a component of another project. The source and documentation are available here: https://github.com/andrewchilds/jQuery.DomOutline
One simple way to do it is to use an outline instead of a border:
.highlight { outline: 4px solid #07C; }
Just add and remove that class to any element you want to select/deselect (code below is not properly tested):
document.body.addEventListener("mouseover", function(e) {
e.stopPropagation();
e.target.addEventListener("mouseout", function (e) {
e.target.className = e.target.className.replace(new RegExp(" highlight\\b", "g"), "");
});
e.target.className += " highlight";
});
Since you are using an outline, (which is supported by Chrome) instead of a border, elements will not jump around. I'm using something similar in my EasyReader Extension.
HTML Element Picker (Vanilla JS)
Pick and highlight any HTML element on a page with only Vanilla JS! Tested in Chrome, FF, and Opera, doesn't work in IE.
How it works:
What you need is actually very simple. You can just create an empty div box with a background in JS and move it around to highlight on top of hovered elements.
Here's the JS code:
const hoverBox = document.createElement("div");
console.log("hoverBox: ", hoverBox);
hoverBox.style.position = "absolute";
// change to whatever highlight color you want
hoverBox.style.background = "rgba(153, 235, 255, 0.5)";
// avoid blocking the hovered element and its surroundings
hoverBox.style.zIndex = "0";
document.body.appendChild(hoverBox);
let previousTarget;
document.addEventListener("mousemove", (e) => {
let target = e.target;
if (target === hoverBox) {
// the truely hovered element behind the added hover box
const hoveredElement = document.elementsFromPoint(e.clientX, e.clientY)[1];
if (previousTarget === hoveredElement){
// avoid repeated calculation and rendering
return;
} else{
target = hoveredElement;
}
} else{
previousTarget = target;
}
const targetOffset = target.getBoundingClientRect();
const targetHeight = targetOffset.height;
const targetWidth = targetOffset.width;
// add a border around hover box
const boxBorder = 5;
hoverBox.style.width = targetWidth + boxBorder * 2 + "px";
hoverBox.style.height = targetHeight + boxBorder * 2 + "px";
// need scrollX and scrollY to account for scrolling
hoverBox.style.top = targetOffset.top + window.scrollY - boxBorder + "px";
hoverBox.style.left = targetOffset.left + window.scrollX - boxBorder + "px";
});
See Demo
I also made an npm package for the element picker with many more user configurations like background color, border width, transition, etc.
Here's the GitHub page.
I ended up asking in the Firebug group and got some great help:
http://groups.google.com/group/firebug/browse_thread/thread/7d4bd89537cd24e7/2c9483d699efe257?hl=en#2c9483d699efe257
Here is a library that written in pure javascript as an alternative.
TheRoom JS: https://github.com/hsynlms/theroomjs
// theroom information template for target element
var template="";
template += "<div id=\"theroom-info\">";
template += " <span id=\"theroom-tag\"><\/span>";
template += " <span id=\"theroom-id\"><\/span>";
template += " <span id=\"theroom-class\"><\/span>";
template += "<\/div>";
template += "";
template += "<style>";
template += " #theroom-info {";
template += " position: fixed;";
template += " bottom: 0;";
template += " width: 100%;";
template += " left: 0;";
template += " font-family: \"Courier\";";
template += " background-color: #ffffff;";
template += " padding: 10px;";
template += " color: #333333;";
template += " text-align: center;";
template += " box-shadow: 0px 4px 20px rgba(0,0,0,0.3);";
template += " }";
template += "";
template += " #theroom-tag {";
template += " color: #C2185B;";
template += " }";
template += "";
template += " #theroom-id {";
template += " color: #5D4037;";
template += " }";
template += "";
template += " #theroom-class {";
template += " color: #607D8B;";
template += " }";
template += "<\/style>";
var options = {
template: template,
showInfo: true
};
// initialize
theRoom.start(options);
codepen demo
There was a similar question asked on Stackoverflow and it had lots of good answers:
Does anyone know a DOM inspector javascript library or plugin?
For those who are looking for a quick and dirty solution:
http://userscripts.org/scripts/review/3006 is the easiest. Just put the code within <script></script> tags and you are good to go.
https://github.com/josscrowcroft/Simple-JavaScript-DOM-Inspector/blob/master/inspector.js is slightly better and still very easy to integrate in.
For a more sophisticated element inspector, you might want to check out the SelectorGadget as pointed by Udi. The inspector selection code is in http://www.selectorgadget.com/stable/lib/interface.js
Also check this one out:
http://rockingcode.com/tutorial/element-dom-tree-jquery-plugin-firebug-like-functionality/
I found it pretty insightful.. and there's a demo here:
http://rockingcode.com/demos/elemtree/
Hope this helps.
A very basic implementation can be done very easily without jQuery using .onmouseover and e.target:
var last,
bgc;
document.onmouseover = function(e) {
var elem = e.target;
if (last != elem) {
if (last != null) {
last.classList.remove("hovered");
}
last = elem;
elem.classList.add("hovered");
}
}
With the CSS below if you want the children to change background as well:
.hovered,
.hovered * {
cursor: pointer;
color: black;
background-color: red;
}
Demo
If you want to select elements only near the edges (or select the parent near the edges and the element itself everywhere else) you could use .getBoundingClientRect.
var last;
window.addEventListener("mousemove", function(e) {
if(last) {
last.style.background = ''; // empty is enough to restore previous value
}
var elem = e.target;
if(elem === document.body || elem === document.documentElement) {
return;
}
var bb = elem.getBoundingClientRect();
var xr = e.pageX - bb.left; // x relative to elem
var yr = e.pageY - bb.top; // y relative to elem
var ew = 10; // edge width
if(
xr <= ew
|| xr >= bb.width - ew
|| yr <= ew
|| yr >= bb.height - ew
){
elem.style.background = 'red';
last = elem;
}
});
Paired with some borders, this can be pretty usable for selection. Demo
What you need to do is to create 4 elements for the highlighting. They will form an empty square, and so your mouse events are free to fire. This is similar to this overlay example I've made.
The difference is that you only need the four elements (no resize markers), and that the size and position of the 4 boxes are a bit different (to mimick the red border). Then you can use event.target in your event handler, because it gets the real topmost element by default.
Another approach is to hide the exra element, get elementFromPoint, calculate then put it back.
They're faster than light, I can tell you. Even Einstein would agree :)
1.) elementFromPoint overlay/borders - [Demo1] FF needs v3.0+
var box = $("<div class='outer' />").css({
display: "none", position: "absolute",
zIndex: 65000, background:"rgba(255, 0, 0, .3)"
}).appendTo("body");
var mouseX, mouseY, target, lastTarget;
// in case you need to support older browsers use a requestAnimationFrame polyfill
// e.g: https://gist.github.com/paulirish/1579671
window.requestAnimationFrame(function frame() {
window.requestAnimationFrame(frame);
if (target && target.className === "outer") {
box.hide();
target = document.elementFromPoint(mouseX, mouseY);
}
box.show();
if (target === lastTarget) return;
lastTarget = target;
var $target = $(target);
var offset = $target.offset();
box.css({
width: $target.outerWidth() - 1,
height: $target.outerHeight() - 1,
left: offset.left,
top: offset.top
});
});
$("body").mousemove(function (e) {
mouseX = e.clientX;
mouseY = e.clientY;
target = e.target;
});
2.) mouseover borders - [Demo2]
var box = new Overlay();
$("body").mouseover(function(e){
var el = $(e.target);
var offset = el.offset();
box.render(el.outerWidth(), el.outerHeight(), offset.left, offset.top);
});​
/**
* This object encapsulates the elements and actions of the overlay.
*/
function Overlay(width, height, left, top) {
this.width = this.height = this.left = this.top = 0;
// outer parent
var outer = $("<div class='outer' />").appendTo("body");
// red lines (boxes)
var topbox = $("<div />").css("height", 1).appendTo(outer);
var bottombox = $("<div />").css("height", 1).appendTo(outer);
var leftbox = $("<div />").css("width", 1).appendTo(outer);
var rightbox = $("<div />").css("width", 1).appendTo(outer);
// don't count it as a real element
outer.mouseover(function(){
outer.hide();
});
/**
* Public interface
*/
this.resize = function resize(width, height, left, top) {
if (width != null)
this.width = width;
if (height != null)
this.height = height;
if (left != null)
this.left = left;
if (top != null)
this.top = top;
};
this.show = function show() {
outer.show();
};
this.hide = function hide() {
outer.hide();
};
this.render = function render(width, height, left, top) {
this.resize(width, height, left, top);
topbox.css({
top: this.top,
left: this.left,
width: this.width
});
bottombox.css({
top: this.top + this.height - 1,
left: this.left,
width: this.width
});
leftbox.css({
top: this.top,
left: this.left,
height: this.height
});
rightbox.css({
top: this.top,
left: this.left + this.width - 1,
height: this.height
});
this.show();
};
// initial rendering [optional]
// this.render(width, height, left, top);
}

How to get an element's top position relative to the browser's viewport?

I want to get the position of an element relative to the browser's viewport (the viewport in which the page is displayed, not the whole page). How can this be done in JavaScript?
Many thanks
The existing answers are now outdated. The native getBoundingClientRect() method has been around for quite a while now, and does exactly what the question asks for. Plus it is supported across all browsers (including IE 5, it seems!)
From MDN page:
The returned value is a TextRectangle object, which contains read-only left, top, right and bottom properties describing the border-box, in pixels, with the top-left relative to the top-left of the viewport.
You use it like so:
var viewportOffset = el.getBoundingClientRect();
// these are relative to the viewport, i.e. the window
var top = viewportOffset.top;
var left = viewportOffset.left;
On my case, just to be safe regarding scrolling, I added the window.scroll to the equation:
var element = document.getElementById('myElement');
var topPos = element.getBoundingClientRect().top + window.scrollY;
var leftPos = element.getBoundingClientRect().left + window.scrollX;
That allows me to get the real relative position of element on document, even if it has been scrolled.
var element = document.querySelector('selector');
var bodyRect = document.body.getBoundingClientRect(),
elemRect = element.getBoundingClientRect(),
offset = elemRect.top - bodyRect.top;
Edit: Add some code to account for the page scrolling.
function findPos(id) {
var node = document.getElementById(id);
var curtop = 0;
var curtopscroll = 0;
if (node.offsetParent) {
do {
curtop += node.offsetTop;
curtopscroll += node.offsetParent ? node.offsetParent.scrollTop : 0;
} while (node = node.offsetParent);
alert(curtop - curtopscroll);
}
}
The id argument is the id of the element whose offset you want. Adapted from a quirksmode post.
jQuery implements this quite elegantly. If you look at the source for jQuery's offset, you'll find this is basically how it's implemented:
var rect = elem.getBoundingClientRect();
var win = elem.ownerDocument.defaultView;
return {
top: rect.top + win.pageYOffset,
left: rect.left + win.pageXOffset
};
function inViewport(element) {
let bounds = element.getBoundingClientRect();
let viewWidth = document.documentElement.clientWidth;
let viewHeight = document.documentElement.clientHeight;
if (bounds['left'] < 0) return false;
if (bounds['top'] < 0) return false;
if (bounds['right'] > viewWidth) return false;
if (bounds['bottom'] > viewHeight) return false;
return true;
}
source
The function on this page will return a rectangle with the top, left, height and width co ordinates of a passed element relative to the browser view port.
localToGlobal: function( _el ) {
var target = _el,
target_width = target.offsetWidth,
target_height = target.offsetHeight,
target_left = target.offsetLeft,
target_top = target.offsetTop,
gleft = 0,
gtop = 0,
rect = {};
var moonwalk = function( _parent ) {
if (!!_parent) {
gleft += _parent.offsetLeft;
gtop += _parent.offsetTop;
moonwalk( _parent.offsetParent );
} else {
return rect = {
top: target.offsetTop + gtop,
left: target.offsetLeft + gleft,
bottom: (target.offsetTop + gtop) + target_height,
right: (target.offsetLeft + gleft) + target_width
};
}
};
moonwalk( target.offsetParent );
return rect;
}
You can try:
node.offsetTop - window.scrollY
It works on Opera with viewport meta tag defined.
I am assuming an element having an id of btn1 exists in the web page, and also that jQuery is included. This has worked across all modern browsers of Chrome, FireFox, IE >=9 and Edge.
jQuery is only being used to determine the position relative to document.
var screenRelativeTop = $("#btn1").offset().top - (window.scrollY ||
window.pageYOffset || document.body.scrollTop);
var screenRelativeLeft = $("#btn1").offset().left - (window.scrollX ||
window.pageXOffset || document.body.scrollLeft);
Thanks for all the answers. It seems Prototype already has a function that does this (the page() function). By viewing the source code of the function, I found that it first calculates the element offset position relative to the page (i.e. the document top), then subtracts the scrollTop from that. See the source code of prototype for more details.
Sometimes getBoundingClientRect() object's property value shows 0 for IE. In that case you have to set display = 'block' for the element. You can use below code for all browser to get offset.
Extend jQuery functionality :
(function($) {
jQuery.fn.weOffset = function () {
var de = document.documentElement;
$(this).css("display", "block");
var box = $(this).get(0).getBoundingClientRect();
var top = box.top + window.pageYOffset - de.clientTop;
var left = box.left + window.pageXOffset - de.clientLeft;
return { top: top, left: left };
};
}(jQuery));
Use :
var elementOffset = $("#" + elementId).weOffset();
Based on Derek's answer.
/**
* Gets element's x position relative to the visible viewport.
*/
function getAbsoluteOffsetLeft(el) {
let offset = 0;
let currentElement = el;
while (currentElement !== null) {
offset += currentElement.offsetLeft;
offset -= currentElement.scrollLeft;
currentElement = currentElement.offsetParent;
}
return offset;
}
/**
* Gets element's y position relative to the visible viewport.
*/
function getAbsoluteOffsetTop(el) {
let offset = 0;
let currentElement = el;
while (currentElement !== null) {
offset += currentElement.offsetTop;
offset -= currentElement.scrollTop;
currentElement = currentElement.offsetParent;
}
return offset;
}
Here is something for Angular2 +. Tested on version 13
event.srcElement.getBoundingClientRect().top;

jQuery x y document coordinates of DOM object

I need to get the X,Y coordinates (relative to the document's top/left) for a DOM element. I can't locate any plugins or jQuery property or method that can give these to me. I can get the top and left of the DOM element, but that can be either relative to its current container/parent or to document.
you can use Dimensions plugin [Deprecated... included in jQuery 1.3.2+]
offset()
Get the current offset of the first matched element, in pixels, relative to the document.
position()Gets the top and left position of an element relative to its offset parent.
knowing this, then it's easy... (using my little svg project as an example page)
var x = $("#wrapper2").offset().left;
var y = $("#wrapper2").offset().top;
console.log('x: ' + x + ' y: ' + y);
output:
x: 53 y: 177
hope it helps what you're looking for.
here's an image of offset() and position()
using XRay
using Web Developer toolbar
My solution is a plugin with "workarounds" :+D .
But Works!
jQuery.fn.getPos = function(){
var o = this[0];
var left = 0, top = 0, parentNode = null, offsetParent = null;
offsetParent = o.offsetParent;
var original = o;
var el = o;
while (el.parentNode != null) {
el = el.parentNode;
if (el.offsetParent != null) {
var considerScroll = true;
if (window.opera) {
if (el == original.parentNode || el.nodeName == "TR") {
considerScroll = false;
}
}
if (considerScroll) {
if (el.scrollTop && el.scrollTop > 0) {
top -= el.scrollTop;
}
if (el.scrollLeft && el.scrollLeft > 0) {
left -= el.scrollLeft;
}
}
}
if (el == offsetParent) {
left += o.offsetLeft;
if (el.clientLeft && el.nodeName != "TABLE") {
left += el.clientLeft;
}
top += o.offsetTop;
if (el.clientTop && el.nodeName != "TABLE") {
top += el.clientTop;
}
o = el;
if (o.offsetParent == null) {
if (o.offsetLeft) {
left += o.offsetLeft;
}
if (o.offsetTop) {
top += o.offsetTop;
}
}
offsetParent = o.offsetParent;
}
}
return {
left: left,
top: top
};
};
Usage:
var p = $("#wrapper2").getPos();
alert("top:"+p.top+"; left:"+p.left);
The offset function will do that for you.
Here is the example they give:
var p = $("p:last");
var offset = p.offset();
p.html( "left: " + offset.left + ", top: " + offset.top );

Categories

Resources