How can I find out what percentage of the vertical scrollbar a user has moved through at any given point?
It's easy enough to trap the onscroll event to fire when the user scrolls down the page, but how do I find out within that event how far they have scrolled? In this case, the percentage particularly is what's important. I'm not particularly worried about a solution for IE6.
Do any of the major frameworks (Dojo, jQuery, Prototype, Mootools) expose this in a simple cross-browser compatible way?
Oct 2016: Fixed. Parentheses in jsbin demo were missing from answer. Oops.
Chrome, Firefox, IE9+. Live Demo on jsbin
var h = document.documentElement,
b = document.body,
st = 'scrollTop',
sh = 'scrollHeight';
var percent = (h[st]||b[st]) / ((h[sh]||b[sh]) - h.clientHeight) * 100;
As function:
function getScrollPercent() {
var h = document.documentElement,
b = document.body,
st = 'scrollTop',
sh = 'scrollHeight';
return (h[st]||b[st]) / ((h[sh]||b[sh]) - h.clientHeight) * 100;
}
If you prefer jQuery (original answer):
$(window).on('scroll', function(){
var s = $(window).scrollTop(),
d = $(document).height(),
c = $(window).height();
var scrollPercent = (s / (d - c)) * 100;
console.clear();
console.log(scrollPercent);
})
html{ height:100%; }
body{ height:300%; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I think I found a good solution that doesn't depend on any library:
/**
* Get current browser viewpane heigtht
*/
function _get_window_height() {
return window.innerHeight ||
document.documentElement.clientHeight ||
document.body.clientHeight || 0;
}
/**
* Get current absolute window scroll position
*/
function _get_window_Yscroll() {
return window.pageYOffset ||
document.body.scrollTop ||
document.documentElement.scrollTop || 0;
}
/**
* Get current absolute document height
*/
function _get_doc_height() {
return Math.max(
document.body.scrollHeight || 0,
document.documentElement.scrollHeight || 0,
document.body.offsetHeight || 0,
document.documentElement.offsetHeight || 0,
document.body.clientHeight || 0,
document.documentElement.clientHeight || 0
);
}
/**
* Get current vertical scroll percentage
*/
function _get_scroll_percentage() {
return (
(_get_window_Yscroll() + _get_window_height()) / _get_doc_height()
) * 100;
}
This should do the trick, no libraries required:
function currentScrollPercentage()
{
return ((document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight) * 100);
}
These worked for me perfectly in Chrome 19.0, FF12, IE9:
function getElementScrollScale(domElement){
return domElement.scrollTop / (domElement.scrollHeight - domElement.clientHeight);
}
function setElementScrollScale(domElement,scale){
domElement.scrollTop = (domElement.scrollHeight - domElement.clientHeight) * scale;
}
A Typescript implementation.
function getScrollPercent(event: Event): number {
const {target} = event;
const {documentElement, body} = target as Document;
const {scrollTop: documentElementScrollTop, scrollHeight: documentElementScrollHeight, clientHeight} = documentElement;
const {scrollTop: bodyScrollTop, scrollHeight: bodyScrollHeight} = body;
const percent = (documentElementScrollTop || bodyScrollTop) / ((documentElementScrollHeight || bodyScrollHeight) - clientHeight) * 100;
return Math.ceil(percent);
}
If you're using Dojo, you can do the following:
var vp = dijit.getViewport();
return (vp.t / (document.documentElement.scrollHeight - vp.h));
Which will return a value between 0 and 1.
This question has been here for a long time, I know, but I stumbled onto it while trying to solve the same problem. Here is how I solved it, in jQuery:
First, I wrapped the thing I wanted to scroll in a div (not semantic, but it helps). Then set the overflow and height on the wrapper.
<div class="content-wrapper" style="overflow: scroll; height:100px">
<div class="content">Lot of content that scrolls</div>
</div>
Finally I was able to calculate the % scroll from these metrics:
var $w = $(this),
scroll_top = $w.scrollTop(),
total_height = $w.find(".content").height(),
viewable_area = $w.height(),
scroll_percent = Math.floor((scroll_top + viewable_area) / total_height * 100);
Here is a fiddle with working example: http://jsfiddle.net/prEGf/
Everyone has great answers, but I just needed an answer as one variable. I didn't need an event listener, I just wanted to get the scrolled percentage. This is what I got:
const scrolledPercentage =
window.scrollY / (document.documentElement.scrollHeight - document.documentElement.clientHeight)
document.addEventListener("scroll", function() {
const height = window.scrollY / (document.documentElement.scrollHeight - document.documentElement.clientHeight)
document.getElementById("height").innerHTML = `Height: ${height}`
})
.container {
position: relative;
height: 200vh;
}
.sticky-div {
position: sticky;
top: 0;
}
<!DOCType>
<html>
<head>
</head>
<body>
<div id="container" class="container">
<div id="height" class="sticky-div">
Height: 0
</div>
</div>
</body>
First attach an event listener to some document you want to keep track
yourDocument.addEventListener("scroll", documentEventListener, false);
Then:
function documentEventListener(){
var currentDocument = this;
var docsWindow = $(currentDocument.defaultView); // This is the window holding the document
var docsWindowHeight = docsWindow.height(); // The viewport of the wrapper window
var scrollTop = $(currentDocument).scrollTop(); // How much we scrolled already, in the viewport
var docHeight = $(currentDocument).height(); // This is the full document height.
var howMuchMoreWeCanScrollDown = docHeight - (docsWindowHeight + scrollTop);
var percentViewed = 100.0 * (1 - howMuchMoreWeCanScrollDown / docHeight);
console.log("More to scroll: "+howMuchMoreWeCanScrollDown+"pixels. Percent Viewed: "+percentViewed+"%");
}
My two cents, the accepted answer in a more "modern" way. Works back to IE9 using #babel/preset-env.
// utilities.js
/**
* #param {Function} onRatioChange The callback when the scroll ratio changes
*/
export const monitorScroll = onRatioChange => {
const html = document.documentElement;
const body = document.body;
window.addEventListener('scroll', () => {
onRatioChange(
(html.scrollTop || body.scrollTop)
/
((html.scrollHeight || body.scrollHeight) - html.clientHeight)
);
});
};
Usage:
// app.js
import { monitorScroll } from './utilities';
monitorScroll(ratio => {
console.log(`${(ratio * 100).toFixed(2)}% of the page`);
});
I reviewed all of these up there but they use more complex approaches to solve. I found this through a mathematical formula; brief.
The formula goes Value/Total * 100. Say Total is 200 u wanna know the percentage of 100 out of 200, you do it 100/200 * 100% = 50% (the value)
pageYOffset = The vertical scroll count without including borders. When you scroll down to bottom you get the maximum count.
offsetHeight = The total height of the page including borders!
clientHeight = The height in pixels without borders but not to the end of content!
When u scroll to bottom u get pageyoffset of 1000 for example, whereas offsetHeight of 1200 and clientHeight of 200. 1200 - 200(clientheight) now u get paggeYOffset value in offsetHeight and so scrollPosition300(300 of 1000)/1000 * 100 = 30%.
`pageOffset = window.pageYOffset;
pageHeight = document.documentElement.offsetHeight;
clientHeight = document.documentElement.clientHeight;
percentage = pageOffset / (pageHeight - clientHeight) * 100 + "%";
console.log(percentage)`
The reason why we must do offsetHeight - clientHeight it is because client heights shows all the available content in px without borders, and offsetheight shows the available content including borders, whereas pageYOffset counts the scrolls made; The scrollbar is quite long to count the whole windows it counts the scrolls itself until reaches the end, the available space in scrollbar is in px pageYOffset, so to reach that number you substract offsetHeight - clientHeight to bring to the lower value of pageYOffset.
i'll update when i get on pc, please leave a comment to make it clear so i don't forget! Thanks :)
Using jQuery
$(window).scrollTop();
will get you the scroll position, you can then work out from there what the percentage is based on the window height.
There is also a standard DOM property scrollTop that you can use like document.body.scrollTop however I'm not sure how this behaves cross-browser, I would assume if there are inconsistencies then the jQuery method accounts for these.
var maxScrollTop = messages.get(0).scrollHeight - messages.height();
var scroll = messages.scrollTop() / maxScrollTop; // [0..1]
I found a way to correct a previous answer, so it works in all cases. Tested on Chrome, Firefox and Safari.
(((document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight) || 0) * 100)
This is my first project related with Parallax effects. I have tried to implement parallax libraries but they are not working properly on mac safari, scrolling is not smooth. So i tried to implement custom code which i found on google and its working better than libraries but still scrolling is not smooth. Major scroll effect on mac safari.
Note:- Website is made in PHP, single page website with parallax and animations (wow.js) on page scroll. There are large content on the website, all are images implemented into "section" tags. So there are almost 10 different "section" tags on the page, contains approx. 10 images per "section" tag. Page size is more than 45 MB (with 2 videos playing of 22 MB in size).
Tried solution:- Before that all the images was in PNG format, then i had converted them to JPG format and compressed them also. All images are in higher resolution, out of them some are even 2500*1800. Four different parallax images on different "sections", one parallax image on one "section".
Current Parallax script i am using:-
var scrolled = $(window).scrollTop()
$('.parallax').each(function(index) {
var imageSrc = $(this).data('image-src')
var imageHeight = $(this).data('height')
$(this).css('background-image','url(' + imageSrc + ')')
$(this).css('height', imageHeight)
var initY = $(this).offset().top
var height = $(this).height()
var diff = scrolled - initY
var ratio = Math.round((diff / height) * 100)
$(this).css('background-position','center ' + parseInt(-(ratio * 1.5)) + 'px')
})
$(window).scroll(function() {
var scrolled = $(window).scrollTop();
$('.parallax').each(function(index, element) {
var initY = $(this).offset().top;
var height = $(this).height();
var endY = initY + $(this).height();
var visible = isInViewport(this);
if(visible) {
var diff = scrolled - initY;
var ratio = Math.round((diff / height) * 100);
$(this).css('background-position','center ' + parseInt(-(ratio * 1.5)) + 'px');
}
});
});
function isInViewport(node) {
var rect = node.getBoundingClientRect();
return (
(rect.height > 0 || rect.width > 0) &&
rect.bottom >= 0 &&
rect.right >= 0 &&
rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.left <= (window.innerWidth || document.documentElement.clientWidth)
);
}
Anyone, please help!
I have couple of divs that have an event listener when the document is scrolling and it takes the current Y position of the scroll and times is by a little to move the divs. Like so:
ypos = window.pageYOffset;
contactLinks.style.top = ypos * 3.5 + 'px';
This means that the div will continue going if I keep scrolling.
How can I try to stop the div from going out of the innerHeight?
I tried to initially compare the div contactLinks.style.top to the innerHeight but it kept going to the else block.
This is the code:
let clientHeight = window.innerHeight;
document.addEventListener('scroll', parallex);
function parallex() {
if (contactLinks.style.top >= clientHeight) {
contactLinks.style.top = clientHeight + 'px';
} else {
ypos = window.pageYOffset;
contactLinks.style.top = ypos * 3.5 + 'px';
}
}
I also tried this:
function parallex() {
if (ypos >= clientHeight) {
contactLinks.style.top = clientHeight + 'px';
} else {
ypos = window.pageYOffset;
contactLinks.style.top = ypos * 3.5 + 'px';
}
}
I tried to search it but possibly I did not word it well and didn't have much luck!
contactLinks.style.top is a string (e.g. "700px"), while window.innerHeight is a number (e.g. 700). Comparing them will give unexpected result.
You can use offsetTop:
if (contactLinks.offsetTop >= clientHeight) {
contactLinks.style.top = clientHeight + 'px';
Quick Question, how am i able to set the max font size so that the font wont get too big when resizing? my default font size is 22px, but using this code it goes up higher that the default.
<script>
function resize_text(){
resize = $(window).width()/50;
min = 18;
if(resize > min){
$('p').css('font-size',resize + 'px');
$('h1').css('font-size',resize + 'px');
$('.bottom-list').css('font-size',resize + 'px');
} else {
$('p').css('font-size',min + 'px');
$('h1').css('font-size',min + 'px');
$('.bottom-list').css('font-size',min + 'px');
}
}
$(window).resize(function(){
resize_text();
})
</script>
You are using resize = $(window).width()/50; , where resize value may go beyond 22 .
If your default value is 22 , you may try this code :
resize = 22;
if($(window).width()/50 < 22){
resize = $(window).width()/50
}
Instead of doing this with javascript, wy don't you do this with css?
p{font-size: 100%} or provide anyother percentage criteria as per your requirement.
I think it will resolve your issue.
You can change the line that resize is defined on too:
newsize = $(window).width()/50;
resize = newsize > 22 ? 22 : newsize;
I'm using this function:
$(window).bind('load resize scroll',function(e) {
var y = $(window).scrollTop();
$('.tm-parallax').filter(function() {
return $(this).offset().top < (y + $(window).height()) &&
$(this).offset().top + $(this).height() > y;
}).css('background-position', '50% ' + parseInt(-y / 50) + 'px');
});
to achieve parallax effect on background images when scrolling down.
I would like to limit y position to certain value (for example 100px), so background image center stays visible after reaching this value.
Here is the code: http://jsfiddle.net/esedic/vw2n16r8/4/
Because bakcground images are quite large it's best seen on fullscreen: https://jsfiddle.net/esedic/vw2n16r8/4/embedded/result/
Because I'm using parallax background images on multiple elements, I'm looking for solution to set different values for each element (maybe using data attributes?).
Thanks for your help!
You should try reversing its polarity, but try this:
$(window).bind('load resize scroll',function(e) {
var y = $(window).scrollTop();
$('.tm-parallax').filter(function() {
if have return $(this).onset().top < (y + $(window).height()) &&
$(this).onset().top + $(this).height() > y;
}).css('background-position', '50% ' + parseInt(-y / 50) + 'px');
});