RxJs Tutorials - Horizontal Scroll Indicator - Ex 2.2
Tags: rxjs, angular, javascript, tutorial
Published: 2020-11-16 08:31:54

In the first two exercises, we did some basic RxJs, and in this exercise too, we won't do anything fancy we will continue to polish our skills of Rxjs and move on to some advanced exercises later.

Today we are going to create a horizontal scrollbar indicator which indicates how much we have scrolled on a page.

The final result will be something like this:-

To do this let us list down what are the things we need:-

We need the total height of the screen we are on

We need how much height we have scrolled on the screen

Capture scroll event and update the width of the indicator according to the scroll height

All of these are pretty basic and can be done really easily

To get the total height we calculate the total height of the viewport and reduce it by the visible viewport as only when we are past the visible viewport we should move our scrollbar. To calculate how much we have scrolled we can use the scrollTop property on the documentElement. Finally, we divide both these and get the percentage width that the indicator needs to show.

Lets setup the Html first we need an indicator div with some CSS:-
``` html
<div id="indication">&nbsp;</div>
Scroll down!!!
<div class="app" style="position: absolute; margin-top: 3000px;">Boom!</div>
```
We added the boom element with margin-top 3000 so that there is something to scroll. Then we add the required CSS.
``` css
#indication {
    position: fixed;
    width: 5px;
    height: 7px;
    background-color: #ff3366;
    left: 0px;
    right: 0px;
    top: 0px;
    z-index: 2;
} 
```
Then we get the documentElement and the indicator element:-

const indicator = document.getElementById("indication");
const html = document.documentElement;

Now we create a function which will return the width of the indicator:-
``` ts
const getScrollPercentage = () => {
  const winScroll = html.scrollTop;
  const height = html.scrollHeight - html.clientHeight;
  return (winScroll / height) * 100;
};
```
Now we only check the scroll event on the by the fromEvent on the document and after that, we can introduce the getScrollPercentage function into the stream using the tap operator but as I think we need to get the indicator which we did above and set its width to the set scroll percentage. Okay so let's set it
``` ts
fromEvent(document, "scroll")
  .pipe(
    tap(_ => (indicator.style.width = getScrollPercentage() + "%"))
  )
  .subscribe();
```
but scroll runs so often that we should not run the getScrollPercentage function. So, I thought we can use the debounceTime function which we used in the first exercise but upon using it I felt that the scrollbar is not that smooth after its usage. So I searched for debouceTime alternatives and found out throttleTime using this in place of debounceTime makes it much smoother. So, the difference between throtteTime and debounceTime is the same as throttling and debouncing. While debouncing we keep track of the last value during the time period and an eventis emitted after a certain given time period is passed after an event but in throttling, we drop the events in a particular time period. This means that throttle emits an event every 20ms in our case and debounce will emit only when 20ms have passed after the last scrolling event.

So here is our final result.
``` ts
// Import stylesheets
import "./style.css";

// RxJS v6+
import { fromEvent } from "rxjs";
import { throttleTime, tap } from "rxjs/operators";

const indicator = document.getElementById("indication");

const html = document.documentElement;

const getScrollPercentage = () => {
  const winScroll = html.scrollTop;
  const height = html.scrollHeight - html.clientHeight;
  return (winScroll / height) * 100;
};

fromEvent(document, "scroll")
  .pipe(
    throttleTime(20),
    tap(_ => (indicator.style.width = getScrollPercentage() + "%"))
  )
  .subscribe();
```
You can check the results here.

Do let me know if you have any comments or queries.

If you like my work please support me at https://www.buymeacoffee.com/ajitsinghkaler