77

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?

Spikatrix
  • 20,225
  • 7
  • 37
  • 83
majelbstoat
  • 12,889
  • 4
  • 28
  • 26

14 Answers14

144

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>
vsync
  • 118,978
  • 58
  • 307
  • 400
Phil Ricketts
  • 8,236
  • 3
  • 28
  • 31
  • Have you tried in IE9 before saying it works? http://stackoverflow.com/questions/2717252/document-body-scrolltop-is-always-0-in-ie-even-when-scrolling you need document.documentElement ? document.documentElement.scrollTop : document.body.scrollTop; for scrollTop for ie 9 – user627283 Nov 13 '15 at 20:27
  • 1
    I've added an optimal IE9+ version with live demo, and restored the original jQuery solution too. – Phil Ricketts Nov 21 '15 at 00:35
  • @PhilRicketts the jQuery one or the vanilla? because i tried the vanilla – dcohenb Mar 04 '16 at 09:09
  • 1
    For me, it kinda worked on Chrome, but, did not work on latest Firefox as well. It gives NaN. I am referring to the vanilla javascript. Any thoughts? @PhilRicketts – bozzmob Jun 20 '16 at 13:48
  • It gives NaN when checked on Phone format. Any solution for that? – Abhishek Kulshrestha Feb 24 '17 at 10:45
  • Its giving NaN because scrollHeight is the same as clientHeight, resulting in div by zero – Bernardo Dal Corno Mar 02 '18 at 06:44
  • @Z.Khullah The lack of any variable checking in the code made me wary of the answer - thanks for commenting and letting us know of the issues – Drenai May 16 '18 at 23:59
  • 1
    I'm getting `Infinity` when I try this. Found that `document.documentElement.scrollHeight` and `document.documentElement.clientHeight` are equal resulting in division by zero. I'm using Chromium Version 72.0.3626.121 (Official Build) Built on Ubuntu – Spikatrix Mar 16 '19 at 05:11
  • 3
    Fixed the [`Infinity` issue](https://stackoverflow.com/questions/2387136/cross-browser-method-to-determine-vertical-scroll-percentage-in-javascript#comment97125199_8028584) by adding this css code: `html { height: 100%; }` – Spikatrix Mar 16 '19 at 05:23
  • 14
    Why are you using such short variable names? It makes the code instantly unreadable. It looks like minified JS to me. – Rudey Jan 29 '20 at 17:48
  • You should explain why `document.body` and `clientHeight` are needed in your answer. – baptx Nov 14 '20 at 21:00
  • @Rudey I used this one-liner without variable instead, it is fine for using it in a web console and more readable: `document.documentElement["scrollTop"] / (document.documentElement["scrollHeight"] - document.documentElement.clientHeight) * 100` – baptx Jan 24 '21 at 14:36
  • When this failed to work on my site, I found the next answer by eduardo [https://stackoverflow.com/a/9348993/4526479](here) did work. – Kyle Baker Aug 12 '21 at 19:58
  • This answer is from 10 years ago. The short variables were to show the algorithm. Nice attitude @Rudey. – Phil Ricketts Oct 09 '21 at 14:44
  • I found sometimes the scroll percent will be less than 100, and sometimes it will be greater than 100. It's hard to get a perfect 100, unless you don't need to scroll. – xue zhang Feb 06 '23 at 08:33
  • @Rudey No offense, but if that is enough to deter your ability to read the code then I suggest you spend some time practicing JavaScript... – Kröw Apr 08 '23 at 19:16
33

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;
}
Eduardo
  • 22,574
  • 11
  • 76
  • 94
  • Perfect Answer in pure Javascript..! – Prasath K Feb 03 '15 at 15:22
  • I tried other solutions , but this only worked perfectly for me , Thanks – Keval Nov 12 '17 at 05:45
  • This solution does not give a 0%-100% result because it adds the viewport height at the start, to make it 0-100 you should subtract the height from the document total height: _get_window_Yscroll() / (_get_doc_height() - _get_window_height()) – Caio Vertematti Apr 23 '20 at 13:31
  • 1
    @CaioVertematti that is the goal. It shows how much of the total page the user has seen. If the user didn't scroll but the whole page fits the viewport it shows 100. In theory it can give you 0 if the page is very long and/or if the viewport is very short. – Eduardo Apr 25 '20 at 00:20
16

This should do the trick, no libraries required:

function currentScrollPercentage()
{
    return ((document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight) * 100);
}
Mark Bell
  • 28,985
  • 26
  • 118
  • 145
  • This is getting close, thanks. However, in Chrome at least, both document.documentElement.scrollHeight and document.documentElement.clientHeight equate to the same value (height of the content), so the denominator is always zero. – majelbstoat Mar 05 '10 at 14:19
  • Ah, yes: edited the code now, try that! There's an odd issue in WebKit browsers, see here: http://code.google.com/p/chromium/issues/detail?id=2891 – Mark Bell Mar 05 '10 at 14:22
  • Still no worky :/ I'm pretty sure the error is on the bottom half of the division. (document.documentElement.scrollHeight - document.documentElement.clientHeight) always equals zero in Chrome. Seems like a bug? – majelbstoat Mar 05 '10 at 14:40
  • 3
    Hmm, just tested and it works fine in Chrome, IE and Firefox for me. Are you using the official latest version of Chrome? – Mark Bell Mar 05 '10 at 15:03
  • 1
    Chrome version 5.0.335.1 beta. The problem is definitely that the denominator evaluates to zero, because the function (exactly as copied and pasted) always returns Infinity :/ – majelbstoat Mar 05 '10 at 15:11
  • Found a solution using Dojo. Definitely on the right track here though - just a question mark over what different browsers consider "clientHeight" to mean, I think. – majelbstoat Mar 05 '10 at 15:27
  • Thanks for your pointers on this. In case you're interested in what this was being used for: http://jamietalbot.com/projects/css/morning-sunset/ . It's nice sometimes to see the real world effects of potentially academic questions and answers... (But feel free to delete this comment if you think it's inappropriate self-promotion). – majelbstoat Mar 10 '10 at 22:45
  • And this simpler version is working for me also in Google Chrome: `document.body.scrollTop * 100 / (document.documentElement.scrollHeight - document.documentElement.clientHeight)` – cprcrack Feb 07 '13 at 01:12
  • I'm getting `Infinity` when I try this. Found that `document.documentElement.scrollHeight` and `document.documentElement.clientHeight` are equal resulting in division by zero. I'm using Chromium Version 72.0.3626.121 (Official Build) Built on Ubuntu – Spikatrix Mar 16 '19 at 05:11
  • Fixed the [`Infinity` issue](https://stackoverflow.com/questions/2387136/cross-browser-method-to-determine-vertical-scroll-percentage-in-javascript#comment97125199_8028584) by adding this css code: `html { height: 100%; }` – Spikatrix Mar 16 '19 at 05:23
11

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;
    }
toske
  • 1,744
  • 13
  • 24
  • Thanks @toske, I found several complex answers that didn't work until I found your answer. Just what I was looking for. – Rocky Sims Jul 21 '19 at 03:48
6

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>
lua_python_java
  • 359
  • 2
  • 8
3

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);
}
David
  • 168
  • 2
  • 5
  • Uncaught TypeError: Cannot destructure property 'scrollTop' of 'documentElement' as it is undefined – lhk May 19 '20 at 09:05
2

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.

majelbstoat
  • 12,889
  • 4
  • 28
  • 26
  • 3
    Because the other ones on the same day didn't give a full answer, and every other answer came more than a year after I marked it correct :) – majelbstoat Aug 26 '13 at 00:56
  • @majelbstoat If another's better in your opinion now, there's nothing wrong with moving the check. "[*You may change which answer is accepted, or simply un-accept the answer, at any time.*](http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work/5235#5235)" Good question maintenance is good. ;^) – ruffin Jan 26 '17 at 12:31
2

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/

timemachine3030
  • 362
  • 1
  • 9
1

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+"%");
}
Nathan B
  • 1,625
  • 1
  • 17
  • 15
0

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`);
});
gremo
  • 47,186
  • 75
  • 257
  • 421
0

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 :)

SeiTaku
  • 1
  • 1
-1

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.

roryf
  • 29,592
  • 16
  • 81
  • 103
  • Is window height going to return me the height of the window (e.g. 768px for a standard resolution) or the height of the content (variable, depending on the amount of text in the page). – majelbstoat Mar 05 '10 at 14:00
  • window.height will return height of viewport, document.height will return height of content. Just tested these in IE without success, but $(document).height() does work cross-browser – roryf Mar 05 '10 at 15:11
  • This doesn't work if you pinch to expand a page in Safari. It gets multiplied to a number that does not directly correspond to the level of zoom. – Andy Swift Feb 13 '15 at 10:46
-1
var maxScrollTop = messages.get(0).scrollHeight - messages.height();
var scroll = messages.scrollTop() / maxScrollTop; // [0..1]
Denis535
  • 3,407
  • 4
  • 25
  • 36
-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)
Alexandru R
  • 8,560
  • 16
  • 64
  • 98