12

I need to calculate the offsetRight of a DOM object. I already have some rather simple code for getting the offsetLeft, but there is no javascript offsetRight property. If I add the offsetLeft and offsetWidth, will that work? Or is there a better way?

function getOffsetLeft(obj)
{
    if(obj == null)
        return 0;
    var offsetLeft = 0;
    var tmp = obj;
    while(tmp != null)
    {
        offsetLeft += tmp.offsetLeft;
        tmp = tmp.offsetParent;
    }
    return offsetLeft;
}

function getOffsetRight(obj)
{
    if (obj == null)
        return 0;
    var offsetRight = 0;
    var tmp = obj;
    while (tmp != null)
    {
        offsetRight += tmp.offsetLeft + tmp.offsetWidth;
        tmp = tmp.offsetParent;
    }
    return offsetRight;    
}
Dan Bailiff
  • 1,513
  • 5
  • 23
  • 38
  • What do you want to do with "offsetRight"? The reason "left" and "top" are there is that the top an left of the window (or any container box, really) are fixed; viewframe stretching/shrinking happens (conceptually) at the right and the bottom. – Pointy Jun 08 '10 at 21:18
  • I have to draw a div behind the current objects, and I'm doing in this is a right to left language. I have to know where the object (text) ends on the right to know where to position the right edge of the div. Since everything in the DOM works left to right, this is a headache. – Dan Bailiff Jun 09 '10 at 22:17

4 Answers4

12

Cannot be more simpler than this:

let offsetright = window.innerWidth - obj.offsetLeft - obj.offsetWidth
0xLogN
  • 3,289
  • 1
  • 14
  • 35
  • 3
    An other way to calculate offsetRight `window.innerWidth - element.getBoundingClientRect().right` – Hasan Sefa Ozalp Apr 21 '21 at 04:59
  • Nice, but this example is too simple. It doesn't account for nested elements such as when some wrapping elements have styled positions of relative. It needs to be recursive or leverage the viewport. It also doesn't account for overflow. – Modular Aug 05 '22 at 23:47
  • @HasanSefaOzalp I like that way too which was what I posted about 4 years ago, but it doesn't account for overflow. – Modular Aug 05 '22 at 23:52
8

UPDATED POST TO CLARIFY SOME GOTCHAS:

// Assuming these variables:
const elem = document.querySelector('div'),
      body = document.body,
      html = document.documentElement;

Here are several approaches:

/* Leveraging the viewport AND accounting for possible overflow to the right */
const offsetRight = body.clientWidth - elem.getBoundingClientRect().right
// OR
const offsetRight = body.scrollWidth - elem.getBoundingClientRect().right
// OR
const offsetRight = html.scrollWidth - elem.getBoundingClientRect().right

OR

/*
*  Likely the safest option:
*  Doesn't depend on the viewport
*  Accounts for overflow to the right
*  Works even if the user is scrolled to the right some
*  NOTE: This ends at the <html> element,
*  but you may want to modify the code to end at the <body>
*/
const getOffsetRight = e => {
    let left = e.offsetWidth + e.offsetLeft;
    const traverse = eRef => {
        eRef = eRef.offsetParent; // `.offsetParent` is faster than `.parentElement`
        if (eRef) {
            left += eRef.offsetLeft;
            traverse(eRef);
        }
    };
    traverse(e);
    return html.scrollWidth - left;
};
const offsetRight = getOffsetRight(elem);

Import considerations:

  • Are you using box-sizing: border-box; for all your elements?
  • Is there margin-left set on the <body> or <html> elements you need to account for?
  • Does the <body> have a fixed width but centered such as with margin: 0 auto;

Those things will help determine which method to use, and if you want to modify the CSS and/or the JavaScript to account for those use cases.

ORIGINAL POST:

A few choices:

  • If you want "offsetRight" relative to the viewport, use element.getBoundingClientRect().right;

  • Your example is good simply subracting the parent width from the element's width + offsetLeft.

  • Lastly, to be relative to the document, and to speed up traversing (offsetParent):

In this example, I'm positioning a pseudo dropdown element below the referenced element, but because I'm avoiding some tricky z-index issues and want to have the element be referenced from the right and expand out left, I had to append it to the body element, and the get the "offsetRight" from the original parent.

...

// Set helper defaults
dropdownElem.style.left = 'auto';
dropdownElem.style.zIndex = '10';

// Get the elem and its offsetParent
let elem = dropdownElemContainer;
let elemOffsetParent = elem.offsetParent;

// Cache widths
let elemWidth = elem.offsetWidth;
let elemOffsetParentWidth = 0;

// Set the initial offsets
let top = elem.offsetHeight; // Because I want to visually append the elem at the bottom of the referenced bottom
let right = 0;

// Loop up the DOM getting the offsetParent elements so you don't have to traverse the entire ancestor tree
while (elemOffsetParent) {
  top += elem.offsetTop;
  elemOffsetParentWidth = elemOffsetParent.offsetWidth;
  right += elemOffsetParentWidth - (elem.offsetLeft + elemWidth); // Most important line like your own example
  // Move up the DOM
  elem = elemOffsetParent;
  elemOffsetParent = elemOffsetParent.offsetParent;
  elemWidth = elemOffsetParentWidth;
}

// Set the position and show the elem
dropdownElem.style.top = top + 'px';
dropdownElem.style.right = right + 'px';
dropdownElem.style.display = 'block';
Modular
  • 6,440
  • 2
  • 35
  • 38
3
//Object references
function getObject(id) {
   var object = null;
   if (document.layers) {
    object = document.layers[id];
   } else if (document.all) {
    object = document.all[id];
   } else if (document.getElementById) {
    object = document.getElementById(id);
   }
   return object;
}
//Get pixel dimensions of screen
function getDimensions(){
    var winW = 630, winH = 460;
    if (document.body && document.body.offsetWidth) {
     winW = document.body.offsetWidth;
     winH = document.body.offsetHeight;
    }
    if (document.compatMode=='CSS1Compat' && document.documentElement && document.documentElement.offsetWidth ) {
     winW = document.documentElement.offsetWidth;
     winH = document.documentElement.offsetHeight;
    }
    if (window.innerWidth && window.innerHeight) {
     winW = window.innerWidth;
     winH = window.innerHeight;
    }
    return{"width":winW, "height":winH}
}
//Get the location of element
function getOffsetRight(elem){
    element=getObject(elem)
    var width = element.offsetWidth
    var right = 0;
    while (element.offsetParent) { 
        right += element.offsetLeft; 
        element = element.offsetParent;
    }
    right += element.offsetLeft;
    right = getDimensions()["width"]-right
    right -= width
    return right
}

This is not bullet-proof but you can usually get the "offsetRight" by calling: getOffsetRight("[object.id]")

0

If you are interested in using some Js library then try the following functionality of prototype js http://api.prototypejs.org/dom/element/offset/

Ifi
  • 540
  • 1
  • 4
  • 12
  • 2
    That's just another way of doing what offsetLeft already does, and still doesn't give me the offsetRight. How is this helpful? – Dan Bailiff Jun 08 '10 at 20:49