Yevhen Klymentiev
dark
light
console
darkness
y.klymentiev@gmail.com
Reusable Snippets|Practical utility code for everyday use — custom-built and ready to share

observeVisibility

Observes visibility changes of a DOM element using IntersectionObserver.

TypeScript
Copied!
1/**
2 * Observes visibility changes of a DOM element using IntersectionObserver.
3 *
4 * @param element - The element to observe.
5 * @param callback - A function called with a boolean indicating visibility.
6 * @param options - Optional IntersectionObserver options.
7 * @returns A cleanup function to stop observing.
8 */
9export function observeVisibility(
10  element: Element,
11  callback: (isVisible: boolean) => void,
12  options?: IntersectionObserverInit
13): () => void {
14  const observer = new IntersectionObserver(([entry]) => {
15    callback(entry.isIntersecting);
16  }, options);
17
18  observer.observe(element);
19
20  return () => observer.disconnect();
21}
  • Efficient Visibility Detection

    Uses the native IntersectionObserver API, which is optimized by the browser and avoids performance issues from manual polling or scroll listeners.

  • Declarative Cleanup

    Returns a cleanup function, making it easy to manage lifecycle and avoid memory leaks in dynamic UI environments.

  • Flexible Configuration

    Supports all standard IntersectionObserverInit options like threshold, root, and rootMargin for fine-tuned visibility control.

  • Lightweight and Focused

    Provides a minimal, composable utility that focuses only on visibility tracking without bundling unrelated logic.

Tests | Examples

TypeScript
Copied!
1let mockObserve: jest.Mock;
2let mockDisconnect: jest.Mock;
3let mockIntersectionObserver: jest.Mock;
4
5beforeAll(() => {
6  mockObserve = jest.fn();
7  mockDisconnect = jest.fn();
8
9  mockIntersectionObserver = jest.fn((callback, _options) => {
10    (mockIntersectionObserver as any)._callback = callback;
11    return {
12      observe: mockObserve,
13      disconnect: mockDisconnect
14    };
15  });
16
17  Object.defineProperty(window, 'IntersectionObserver', {
18    writable: true,
19    configurable: true,
20    value: mockIntersectionObserver
21  });
22});
23
24beforeEach(() => {
25  jest.clearAllMocks();
26});
27
28test('calls observe on the target element', () => {
29  const element = document.createElement('div');
30  const callback = jest.fn();
31
32  observeVisibility(element, callback);
33
34  expect(mockObserve).toHaveBeenCalledWith(element);
35});
36
37test('invokes callback with isIntersecting true', () => {
38  const element = document.createElement('div');
39  const callback = jest.fn();
40
41  observeVisibility(element, callback);
42
43  const entry = { isIntersecting: true };
44  (mockIntersectionObserver as any)._callback([entry]);
45
46  expect(callback).toHaveBeenCalledWith(true);
47});
48
49test('invokes callback with isIntersecting false', () => {
50  const element = document.createElement('div');
51  const callback = jest.fn();
52
53  observeVisibility(element, callback);
54
55  const entry = { isIntersecting: false };
56  (mockIntersectionObserver as any)._callback([entry]);
57
58  expect(callback).toHaveBeenCalledWith(false);
59});
60
61test('returns a cleanup function that disconnects observer', () => {
62  const element = document.createElement('div');
63  const callback = jest.fn();
64
65  const cleanup = observeVisibility(element, callback);
66  cleanup();
67
68  expect(mockDisconnect).toHaveBeenCalled();
69});

Common Use Cases

  • Lazy Loading Content

    Load images, components, or data only when they enter the viewport to improve performance.

  • Triggering Animations

    Start animations or transitions when elements become visible on screen.

  • Tracking Viewport Metrics

    Monitor if promotional banners, ads, or CTAs are seen by the user.

  • Infinite Scroll Implementation

    Detect when a "load more" element enters view to trigger additional data fetching.

  • Sticky Header or UI Adjustments

    Observe sentinel elements to toggle UI behaviors like fixing headers or showing floating actions.

Codebase: Utilities -> Browser & DOM -> observeVisibility | Yevhen Klymentiev