Reactive Sticky Header in Angular
Show And Hide Sticky Header Based on the Scroll Direction
In this article, we will explore a common UI pattern - sticky header that is displayed or hidden based on the direction of the scroll. We will implement this with RxJS and Angular Animations.
First, we get a handle on the scroll stream with the use of fromEvent
observable creator.
fromEvent
Creates an observable sequence by adding an event listener to the matching DOMElement
To get better performance, we throttle the event stream with the use of the throttleTime
operator. We keep the throttle value low as we don’t want to make the showing and hiding of the header to feel unresponsive.
throttleTime
Returns an Observable that emits only the first item emitted by the source Observable during sequential time windows of a specified duration.
Next, we map the window
object to the pageYOffset
, which returns the number of pixels the document is currently scrolled along the vertical axis.
Since we’re interested in the direction of the scrolling, we must compare the subsequent changes to the pageYOffset
as the user scrolls.
To achieve this, we group the values emitted by the scroll stream in pairs with the use of thepairwise
operator.
pairwise
Triggers on the second and subsequent triggerings of the input observable. The Nth triggering of the input observable passes the arguments from the N-1th and Nth triggering as a pair.
Next, we compare the pixel values in each pair. If the second value is lower than the first, the user is scrolling up. Otherwise, he is scrolling down.
Notice that there are a lot of repeated values being emitted. Since we are only interested in the changes of the direction, we can remove the repeated events with the use of the distinctUntilChanged
operator.
distinctUntilChanged
Returns an observable sequence that contains only distinct contiguous elements
Finally, we create two streams: scrollUp$
and scrollDown$
with the use of the filter
operator. We also apply the share
operator to the scroll$
stream, to avoid creating multiple subscriptions.
share
Returns an observable sequence that shares a single subscription to the underlying sequence.
Now it’s time to update the view based on the values emitted by the scrollUp$
and scrollDown$
streams. For this, we will use @angular/animations
.
First, we consume the streams by setting the value of theisVisible
flag. We will use this flag to create the animation trigger.
We want the header to be visible when scrolling up, and to be hidden when scrolling down, so we update the flag accordingly.
Next, we create a host binding for the @toggle
animation trigger. It maps the isVisibility
flag to the desired VisibilityState
. We will create the animation in the next step.
Finally, we implement the animation. We use toggle
as the animation trigger and VisibilityState
to describe the possible states of the header. The state
functions update the header’s opacity
and transform
CSS properties with the use of the style
function.
And that’s it. We have a working reactive sticky header.
Note On Performance
You might consider running the above code outside the Angular Zone to prevent the change detection from being fired too frequently. You can read more about NgZone
in the official documentation.
Complete example: