Scrollable Container
The Scroll Watcher feature provides a centralized system for monitoring and reacting to scroll events occurring on the main browser window or within specific designated scrollable container elements in an Angular application. It allows developers to subscribe to a unified stream of scroll notifications, identify the source of the scroll, and apply performance optimizations like audit time.
Technical Overview
This feature is composed of two primary parts: the LmnScrollWatcherService
and the LmnScrollableContainerDirective
.
LmnScrollWatcherService
is an injectable root service that acts as the central hub for all scroll event monitoring. It manages subscriptions to native scroll events from the browser window and from any elements marked by the LmnScrollableContainerDirective
. The service exposes a primary method, listenScroll()
, which returns an Observable. This Observable emits either the instance of the LmnScrollableContainerDirective
that was scrolled or void
if the scroll event originated from the window. The service intelligently adds or removes the window scroll listener based on whether there are active subscribers, and it manages the registration and unregistration of individual scrollable containers. State management for scroll watchers (like active containers and subscription counts) is handled internally using the getFeatureState
utility.
LmnScrollableContainerDirective
is an attribute directive ([lmnScrollableContainer]
) used to designate specific HTML elements as scrollable areas that should be monitored by the LmnScrollWatcherService
. Upon initialization, the directive automatically registers itself with the service, providing its unique ID (either auto-generated or specified via input) and an Observable of its native scroll events. When the directive is destroyed, it automatically unregisters itself from the service to prevent memory leaks.
Key Features
- Unified Scroll Event Stream: The
LmnScrollWatcherService
provides a singlelistenScroll()
method to subscribe to scroll events from both the window and any registeredLmnScrollableContainerDirective
instances. - Source Identification: Emitted values from
listenScroll()
specify the source: either theLmnScrollableContainerDirective
instance orvoid
for window scrolls. - Specific Container Filtering: The
listenScroll()
method can optionally take anid
parameter to only receive notifications for a specific registered scrollable container. - Performance Optimization:
- An
auditTimeInMS
parameter can be passed tolistenScroll()
to limit the rate of emitted scroll events. - The native window scroll listener is only active when there are subscribers to
listenScroll()
. - Scroll event listeners on individual elements (within the directive) are run outside Angular's zone (
.NgZone runOutsideAngular
) to prevent unnecessary change detection cycles.
- An
- Easy Element Registration: The
LmnScrollableContainerDirective
simplifies the process of marking and registering elements for scroll monitoring. - Automatic Lifecycle Management: The directive handles its own registration with the service on init and unregistration on destroy.
- Observable for Element-Specific Scrolls: The directive itself provides an
elementScrolled()
method and astreamScrollEvent
output that gives direct access to the scroll event observable of its host element.
When to Use
Use the Scroll Watcher feature when:
- You need to implement features that react to scrolling, such as infinite scrolling, lazy-loading images/components, parallax effects, or dynamically changing UI elements based on scroll position (e.g., sticky headers, "back to top" buttons).
- You require a centralized way to listen to scroll events from multiple potential sources (window and various elements).
- You need to identify which specific registered container has scrolled.
- Performance is a concern, and you need to control the frequency of scroll event handling using
auditTime
. - You want a declarative way (using the directive) to mark elements as scrollable and have them automatically integrate with a scroll monitoring system.
Implementation
LmnScrollableContainerDirective
Apply the [lmnScrollableContainer]
directive to an HTML element to mark it as a scrollable container that should be monitored by the LmnScrollWatcherService
.
Inputs:
lmnScrollableContainerId
(aliasid
): Optional (string
, default: auto-generated unique ID like'lmn-scrollable-container-X'
). A unique identifier for the scrollable container. This ID is used when registering with theLmnScrollWatcherService
and can be used to filter events inlistenScroll()
.
Outputs:
streamScrollEvent
: Emits anObservable<
once, providing the stream of native scroll events from the host element. This can be used for direct, element-specific scroll handling if needed.Event >
Public Properties (on directive instance):
element: HTMLElement
: The native HTML element to which the directive is applied.id(): string
: A signal accessor for the directive's unique ID.
Public Methods (on directive instance):
elementScrolled(): Observable<
: Returns an Observable that emits nativeEvent >
objects whenever the host element is scrolled. The underlying observable is shared and replayed.Event
LmnScrollWatcherService
The LmnScrollWatcherService is the central service for listening to all monitored scroll events.
Key Public Methods
listenScroll(auditTimeInMS?: number): Observable<LmnScrollableContainerDirective | void>
- Subscribes to scroll events from both the window and all registered
LmnScrollableContainerDirective
instances. auditTimeInMS?
: Optional (number
, default:0
). If greater than0
, scroll events are emitted at most once per this interval.- Returns an
Observable
. When the window scrolls, it emits void. When a registered container scrolls, it emits the instance of thatLmnScrollableContainerDirective
.
- Subscribes to scroll events from both the window and all registered
listenScroll(auditTimeInMS?: number, id?: string): Observable<LmnScrollableContainerDirective | void>
- Overload that allows listening only to scroll events from a specific registered container (by its
id
) or the window. id?
: Optional (string
). If provided, only events from the container with this ID or window scroll events (if no container matches) will be emitted.- If a container with the given
id
scrolls, its directive instance is emitted. Window scrolls still emitvoid
.
- Overload that allows listening only to scroll events from a specific registered container (by its
register(scrollableContainer: LmnScrollableContainerDirective): void
- Called by the
LmnScrollableContainerDirective
during its initialization to register itself with the service. Developers typically do not call this directly.
- Called by the
unregister(id: string): void
:- Called by the
LmnScrollableContainerDirective
when it's destroyed to unregister itself. Developers typically do not call this directly.
- Called by the