• Blazor
  • ASP.NET Core
  • .NET
  • WebApi

Blazor - IntersectionObserver WebApi

This article will go over the IntersectionObserver WebApi and how to use it within the context of a Blazor application. Checkout the Blazor Wasm Example Website and the GitHub Repository.

Details

The main reason for checking out the IntersectionObserver was to see how the canhorn/EventHorizon.Blazor.TypeScript.Interop.Generator can be used with an Api provided by the Browser. I created a TypeScript definition file that contains the API that can be used by the generator to create an C# interop abstraction. This interop abstraction can then be used by any Blazor Wasm/Server application to gain access to the IntersectionObserver API of the browser.

Example

Checkout a the Static Blazor Wasm Website, also included below you can see the TypeScript definition file and the Source Code generated for the IntersectionObserver. You can checkout the source code in this canhorn/EventHorizon.Blazor.WebApi.IntersectionObserver Github repository. It includes the TypeScript definition file and has the generated project to be viewed as well.

Example of using the generated IntersectionObserver in a Blazor application.

var observer = new IntersectionObserver(
    new ActionCallback<IntersectionObserverEntry[], IntersectionObserver>(
        (entries, other) =>
        {
            var intersectedEntries = entries.Where(a => a.isIntersecting);
            foreach (var intersectedEntry in intersectedEntries)
            {
                Console.WriteLine(intersectedEntry.target.id, intersectedEntry.intersectionRatio);
                last.innerHTML = intersectedEntry.target.id + " : " + intersectedEntry.intersectionRatio;
            }
            return Task.CompletedTask;
        }
    ),
    new IntersectionObserverInitCachedEntity
    {
        // Here is the Element we are Observing for Intersection
        root = parent
    }
);

The TypeScript definition file used to generate the .NET C# project.

class IntersectionObserver {
    readonly root: Element | Document | null;
    readonly rootMargin: string;
    readonly thresholds: number;
    disconnect(): void;
    observe(target: Element): void;
    takeRecords(): IntersectionObserverEntry[];
    unobserve(target: Element): void;
    constructor(callback: (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => void, options?: IntersectionObserverInit);
}

class IntersectionObserverEntry {
    readonly boundingClientRect: DOMRectReadOnly;
    readonly intersectionRatio: number;
    readonly intersectionRect: DOMRectReadOnly;
    readonly isIntersecting: boolean;
    readonly rootBounds: DOMRectReadOnly | null;
    readonly target: Element;
    readonly time: number;
    constructor(intersectionObserverEntryInit: IntersectionObserverEntryInit);
}

interface IntersectionObserverEntryInit {
    boundingClientRect: DOMRectInit;
    intersectionRatio: number;
    intersectionRect: DOMRectInit;
    isIntersecting: boolean;
    rootBounds: DOMRectInit | null;
    target: Element;
    time: number;
}

interface IntersectionObserverInit {
    root?: Element | Document | null;
    rootMargin?: string;
    threshold?: number | number[];
}

interface DOMRectInit {
    height?: number;
    width?: number;
    x?: number;
    y?: number;
}

interface DOMRectReadOnly {
    readonly bottom: number;
    readonly height: number;
    readonly left: number;
    readonly right: number;
    readonly top: number;
    readonly width: number;
    readonly x: number;
    readonly y: number;
    toJSON(): any;
}

/**
 * This is the DOM Element, includes only what is needed by the application to function.
 */
class Element {
    id: string;
    innerHTML: string;
    appendChild(child: Element): void;
}

Example of a Source File Generated
The IntersectionObserver generated C# source code.

/// Generated - Do Not Edit
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using EventHorizon.Blazor.Interop;
using EventHorizon.Blazor.Interop.Callbacks;
using Microsoft.JSInterop;

[JsonConverter(typeof(CachedEntityConverter<IntersectionObserver>))]
public class IntersectionObserver : CachedEntityObject
{
    #region Static Accessors

    #endregion

    #region Static Properties

    #endregion

    #region Static Methods

    #endregion

    #region Accessors

    #endregion

    #region Properties
        private Element __root;
        public Element root
        {
            get
            {
            if(__root == null)
            {
                __root = EventHorizonBlazorInterop.GetClass<Element>(
                    this.___guid,
                    "root",
                    (entity) =>
                    {
                        return new Element() { ___guid = entity.___guid };
                    }
                );
            }
            return __root;
            }
        }

        public string rootMargin
        {
            get
            {
            return EventHorizonBlazorInterop.Get<string>(
                    this.___guid,
                    "rootMargin"
                );
            }
        }

        public decimal thresholds
        {
            get
            {
            return EventHorizonBlazorInterop.Get<decimal>(
                    this.___guid,
                    "thresholds"
                );
            }
        }
    #endregion

    #region Constructor
        public IntersectionObserver() : base() { }

        public IntersectionObserver(
            ICachedEntity entity
        ) : base(entity)
        {
            ___guid = entity.___guid;
        }

        public IntersectionObserver(
            ActionCallback<IntersectionObserverEntry[], IntersectionObserver> callback, IntersectionObserverInit options = null
        )
        {
            var entity = EventHorizonBlazorInterop.New(
                new string[] { "IntersectionObserver" },
                callback, options
            );
            ___guid = entity.___guid;
        }
    #endregion

    #region Methods
        public void disconnect()
        {
            EventHorizonBlazorInterop.Func<CachedEntity>(
                new object[]
                {
                    new string[] { this.___guid, "disconnect" }
                }
            );
        }

        public void observe(Element target)
        {
            EventHorizonBlazorInterop.Func<CachedEntity>(
                new object[]
                {
                    new string[] { this.___guid, "observe" }, target
                }
            );
        }

        public IntersectionObserverEntry[] takeRecords()
        {
            return EventHorizonBlazorInterop.FuncArrayClass<IntersectionObserverEntry>(
                entity => new IntersectionObserverEntry() { ___guid = entity.___guid },
                new object[]
                {
                    new string[] { this.___guid, "takeRecords" }
                }
            );
        }

        public void unobserve(Element target)
        {
            EventHorizonBlazorInterop.Func<CachedEntity>(
                new object[]
                {
                    new string[] { this.___guid, "unobserve" }, target
                }
            );
        }
    #endregion
}
Cody's logo image, it is an abstract of a black hole with a white Event Horizon.

Cody Merritt Anhorn

A Engineer with a passion for Platform Architecture and Tool Development.