Eliaslog.pw

When I started out becoming a web designer, many people told me to read “The design of everyday things” by Don Norman. In the beginning of the book he explains the concept of signifier in the world that unintentionally show additional information to the recipient. A bookmark, for example, not only shows where the reader has left of and should start to read again, it also gives an idea how much progress the reader has already made.

Webbrowsers do have a very comparable feature in the scrollbar, wich was implemented in the time before mouse-wheels in order to give the user the possibility to move the page view. They were a great design addition because the user would also gain a prime instrument to judge his position on the page in general and, in the case of written content, make an assumption on how much time he’d have to spent to finish the read.

Many mobile browsers however have the scrollbar tucked away now, as it takes up much of the precious horizontal space, only showing it when the page is actively scrolled. In the following two steps I will show you how to first, read and calculate the vertical scroll position with Javascript, and then implement this information into a readmeter that shows mobile visitors on your blog posts how much they’ve read.

Step 1 - The math

Consider the following code:

function getScrollPercentage() {
  let percentage = null
  let currScrollPosition = null

  return percentage
}

document.addEventListener("scroll", getScrollPercentage)

We first need to set up a function that eventually returns our percentage and that is called at every 'scroll' event.

In theory the calculation of the percentage is very easy: take the total size of the page (ts) divide it by the current scroll position (sp) minus the view port height (vh): ts / (sp - vh). This would resolve in a nice number that, multiplied with 100, would result in an nice percentage.

Unfortunately, we are on the web and total page size is a tricky bit with different browsers. While document.documentElement.scrollHeight works in many browsers, in Firefox we do need to resolve to document.body.scrollHeight.

To prepare for this anomaly, we introduce four new variables into our function:

function getScrollPercentage() {
  let percentage = null
  let currScrollPosition = null

  let docEl = document.documentElement
  let docBody = document.body

  let st = "scrollTop"
  let sh = "scrollHeight"

  return percentage
}

document.addEventListener("scroll", getScrollPercentage)

We now calculate the scollposition - a value somwhere between 0 and 1 - with this line of code:

currScrollPosition =
  (docEl[st] || docBody[st]) / ((docEl[sh] || docBody[sh]) - docEl.clientHeight)

Notice how we put an OR operator (||) between the bits where we are not sure what the browser has in store for us. If either the document.documentElement.scrollHeight or the document.body.scrollHeight returns null, we’re gonna take the other one. There is no need to do this for docEl.clientHeight, as this works in every browser. We need to substract docEl.clientHeight as the scroll position is calculated at the top of the viewport. If we wouldn’t account for that, the scroll position would never reach 100%, as the top of the viewport never touches the absolute bottom of the page.

This scollPosition now just needs to be converted in to an percentage:

percentage = Math.floor(currScrollPosition * 100)

We multiply it by 100 and use Math.floor() ot round it down for an nice and whole number.

The full code is as follows:

Full code

function getScrollPercentage() {
  let percentage = null
  let currScrollPosition
  let docEl = document.documentElement
  let docBody = document.body

  let st = "scrollTop"
  let sh = "scrollHeight"

  currScrollPosition =
    (docEl[st] || docBody[st]) /
    ((docEl[sh] || docBody[sh]) - docEl.clientHeight)
  percentage = Math.floor(currScrollPosition * 100)

  return percentage
}

document.addEventListener("scroll", getScrollPercentage)

We first set up our variables, then do the calculation. In the end we return the percentage to use it elswhere in the code. You can see this code in action right over at my CodePen.

Step 2 - The visualisation

CodePen Embed

Klick below to show the CodePen as an embed. CodePen will set set several Cookies in your Browser.

CodePen.io Privacy Policy

Now your JS has the information on how much the user has scrolled but we obviously need to share this information with the user.

I’m gonna go over the most commonly used technique, that fills a bar at the top of the screen.

We only need one DOM-Element at the top level of our page:

<div class="readmeter"></div>

We then style it up to stick to the top over the entire width:

.readmeter {
  height: 5px;
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  pointer-events: none;
}

The pointer-events:none; make sure that the readmeter gets out of the way of clicks.

And finally we add this line to the bottom of our “getScrollPercentage” function from step one:

document.querySelector(".readmeter").style.backgroundImage =
  "linear-gradient(to right, rgba(216, 222, 233, .3) 0%, rgba(216, 222, 233, .3)" +
  percentage +
  "% ,rgba(0,0,0,0) " +
  percentage +
  "%, rgba(0,0,0,0) 100%"

This changes the background of our readmeter to a gradient, that, very abruptly, changes colors at the given percentage.

Once again you can see this code in action at my CodePen.

There are many other methods to utilize a scroll position. You can fill up circles instead of bars, trigger SVG animations or show “back-to-top” buttons.

If you come up with an interesting implementation, hit me up over at twitter.com/eliasguenther! I’d love to see your work! Have a good one!

The creation of this post was made possible by coffee.
Buy me a coffee