var offset = $(selector).offset();
The values of offset variable changes if we scroll the page up and down, i want the exact and fixed offset value
One other potential cause is if your <body>
happens to have CSS that sets overflow-x: hidden;
- that completely breaks jQuery's offset()
method.
In that case, $(window).scrollTop()
is always 0, so the accepted answer does not work.
I had a very similar issue to the original question. The offset is kinda tricky. Hopefully, this will help solve your problems as it did mine. I have 3 JSFiddles to demonstrate.
offset().top
of your div element from the window, you will always get a static number (i.e. if the $(window)
is the thing that scrolls and you have a div inside the window with offset().top
applied, the div will always have a static number). This offset is the exact number of pixels the element exists from the top of the $(window)
no matter how far down you scroll. For each of my JSFiddles, I'll see how far down the object $('div#offset')
is from the top of it's scrolling container. In this first example, notice how the number doesn't change: https://jsfiddle.net/BrianIsSecond/ehkgh2gu/
$(window)
isn't the container that scrolls. You instead create a div that has an "overflow-y:scroll;" attribute set. In this case, the $('div#offset').offset().top
acts very differently than the previous example. You'll notice it changes as you scroll. For my JSFiddle example, I simply wrapped everything in div#overflow
that has overflow-y:scroll;
attribute set. See example:https://jsfiddle.net/BrianIsSecond/tcs390h6/ <--- Note too that div#overflow isn't 100% tall. I made it 100px short of 100%. I am using that to illustrate an easy mistake, which I will address next.
$('#overflow').scrollTop()
and add it to $('#offset').offset().top
(i.e. var ep = $('#offset').offset().top + $('#overflow').scrollTop()
). Basically, by adding these two changing numbers together, they 'sorta' cancel each other out, giving you the exact position of the div#offset
element... or do they? Well, this is where the position of the div#overflow
is important! You must, must, must take into account the position of the scrollable container. To do this, take $('#overflow').offset().top
and subtract it from $('#offset').offset().top
before you add $('#overflow').scrollTop()
. This will then factor in the position of the scrollable container from the window. See example:https://jsfiddle.net/BrianIsSecond/yzc5ycyg/
Ultimately, what you are looking for is something like this:
var w = $('#overflow'), // overflow-y:scroll;
e = $('#offset'); // element
$('#overflow').scroll(function(){
var wh = w.height(), // window height
sp = w.scrollTop(), // scroll position
ep = (e.offset().top - w.offset().top) + sp; // element position
console.log(ep);
});
UPDATE (10/11/17): I've created a function to help solve this problem. Enjoy!
/*
function: betterOffset
hint: Allows you to calculate dynamic and static offset whether they are in a div container with overscroll or not.
name: type: option: notes:
@param s (static) boolean required default: true | set false for dynamic
@param e (element) string or object required
@param v (viewer) string or object optional If you leave this out, it will use $(window) by default. What I am calling the 'viewer' is the thing that scrolls (i.e. The element with "overflow-y:scroll;" style.).
@return numeric
*/
function betterOffset(s, e, v) {
// Set Defaults
s = (typeof s == 'boolean') ? s : true;
e = (typeof e == 'object') ? e : $(e);
if (v != undefined) {v = (typeof v == 'object') ? v : $(v);} else {v = null;}
// Set Variables
var w = $(window), // window object
wp = w.scrollTop(), // window position
eo = e.offset().top; // element offset
if (v) {
var vo = v.offset().top, // viewer offset
vp = v.scrollTop(); // viewer position
}
// Calculate
if (s) {
return (v) ? (eo - vo) + vp : eo;
} else {
return (v) ? eo - vo : eo - wp;
}
}
Just wanted to add my answer here after having the same issues:
After getting the behaviour described above I looked at the jQuery documentation and discovered that
jQuery does not support getting the offset coordinates of hidden elements or accounting for borders, margins, or padding set on the body element.
The element I was trying to get the offset of was in fact set to display:none;
giving me a false offset which changed when scrolling (even though the element didn't move).
So make sure you're not trying to get an offset of a hidden element! hope this helps someone :)
In 2015, the 'correct' answer should no longer be used - offset has been modified. Any code that used the above solution will no longer work properly.
Solution: Please upgrade jquery to a newer version (works in 1.11.3). Or... change .offset call to use .position instead.
I am having the same problem while editing a old website the workaround I find out is to use $(selector)[0].offsetTop instead of $(selector).offset().top and it is working fine for me right now.
You could always calculate the offset, factoring in the scroll position:
var offset_t = $(selector).offset().top - $(window).scrollTop();
var offset_l = $(selector).offset().left - $(window).scrollLeft();