spaniel
Options
All
  • Public
  • Public/Protected
  • All
Menu

Spaniel Usage Documentation

For overview and setup, checkout the GitHub repo.

API

Spaniel provides APIs at multiple levels of abstraction for determining when DOM elements are visible, with the lowest level of abstraction being a polyfill for the IntersectionObserver API. Spaniel does not actually send viewport/impression tracking events, but rather should be setup to call your app code that does the actual sending of viewport/impression tracking events.

There are three different classes provided:

  • spaniel.IntersectionObserver - A polyfill for IntersectionObserver.
  • spaniel.SpanielObserver - Same API structure as the IntersectionObserver, but with some added functionality for adding time thresholds in addition to ratio thresholds.
  • spaniel.Watcher - Provides an API for receiving high level events about the visibility state of DOM elements.

spaniel.IntersectionObserver

The main part of the IntersectionObserver API not supported by Spaniel's pollyfill is IntersectionObserver.root

spaniel.SpanielObserver

A superset API of IntersectionObserver. There are three additions:

Thresholds

Instead of threshold being an array of ratios, threshold is an array of objects with the following structure:

let options = {
  threshold: [{
    label: 'impressed',
    ratio: 0.5,
    time: 1000
  }]
}

The example threshold is only considered crossed with the intersection ratio has been met for the specified time in milliseconds. When the threshold is crossed, the resulting entry passed to the observer callback will include the label.

Observer Callback parameter

The observer callback is passed an array of change entries. In addition to the standard entry fields from IntersectionObserver, the following properties have been added:

  • duration - How long the elements intersection ratio was above the threshold before going below the threshold. Only passed for change entries associated with going below the threshold.
  • entering - Boolean describing if the intersection ratio is changing to be above the threshold ratio.
  • label - Which threshold is being crossed
  • payload - Optional payload object provided by observe()

Observe() payload parameter

observe() can take an optional second parameter, an object that is included in the change entry passed to the Observer callback.

Putting it all together

import { SpanielObserver } from 'spaniel';

let observer = new SpanielObserver((entries) => {
  let entry = entries[0];
  if (entry.entering) {
    console.log(`${entry.payload.name} has been at least 50% visible for one second`);
  } else {
    console.log(`${entry.payload.name} was at least 50% visible for ${entry.duration} milliseconds`);
  }
}, {
  threshold: [{
    label: 'impressed',
    ratio: 0.5,
    time: 1000
  }]
});
let target = document.getElementById('my-element');
observer.observe(target, {
  name: 'My Element'
});

// Cleanup when done to prevent memory leaks.
observer.destroy();

spaniel.Watcher

The Watcher API is similar in structure, but simpler compared to the IntersectionObserver. Watcher is streamlined for practical scenarios.

import { Watcher } from 'spaniel';
let watcher = new Watcher({
  time: 100,
  ratio: 0.8
});
let target = document.getElementById('my-element');
watcher.watch(target, (eventName, meta) => {
  console.log(`My element was ${eventName} for ${meta.duration} milliseconds`);
});

watcher.unwatch(myOtherTarget);

// Cleanup memory
watcher.destroy();

There are four different event types passed to the watcher callback:

  • exposed - When at least one pixel of the DOM element is in the viewport
  • visible - When the configured percentage of the DOM element is in the viewport, or when the DOM element takes up the configured percentage of the screen.
  • impressed - When the DOM element has been visible for the configured amount of time.
  • impression-complete - When an impressed element is no longer impressed. This event includes the total duration of time the element was visible.

If no config is passed to the Watcher constructor, only the exposed event is fired.

The Watcher constructor can be passed 3 different options:

  • time - The time threshold
  • ratio - The ratio threshold
  • rootMargin - The rootMargin in object form.
  • root - The root element with respect to which we want to watch the target. By default it is window.

Utility API

Under the hood, Spaniel uses requestAnmiationFrame to preform microtask scheduling. Spaniel does not use mutation observers, scroll listeners, or resize listeners. Instead, requestAnmiationFrame polling is used for performance reasons.

Spaniel exposes an API for hooking into the built-in requestAnmiationFrame task scheduling engine, or even setting your own requestAnmiationFrame task scheduling engine.

import { on, off, scheduleRead, scheduleWork } from 'spaniel';

// Do something on scroll
on('scroll', (frame) => {
  console.log('I scrolled to ' + frame.scrollTop);
});

function onResize(frame) {
  console.log('Viewport is ' + frame.width + 'px wide');
}

// Do something on window resize
on('resize', onResize);

// Stop watching resize
off('resize', onResize);

// Triggered after all spaniel observer callbacks have fired during `unload`
on('destroy', flushBeacons);

// Proxy to visibilitychange API
on('show', onTabFocus);
on('hide', onTabUnfocus);

scheduleRead(() => {
  console.log('This will get executed during the DOM read phase of the rAF loop');
});

scheduleWork(() => {
  console.log('This will get executed during the write/mutation phase of the rAF loop');
});

With any task engine involving the DOM, DOM reads and DOM writes should be batched seperately. For this reason, it's important that any work that forces a browser layout be scheduled via scheduleRead(), while any work that modifies the layout should be scheduled via scheduleWork().

Using an external requestAnmiationFrame engine

If you'd like to use a custom requestAnmiationFrame polling/task engine, use setGlobalEngine(engine), where engine is an object that implements the EngineInterface. For example, you can checkout Spaniel's internal Engine implementation or ember-spaniel's implementation that hooks into Ember's runloop.

Copyright 2017 LinkedIn Corp. All rights reserved.

Index

Type aliases

DOMHighResTimeStamp

DOMHighResTimeStamp: number

DOMString

DOMString: string

EventName

EventName: "impressed" | "exposed" | "visible" | "impression-complete"

WatcherCallback

WatcherCallback: function

Type declaration

    • (eventName: EventName, callback: WatcherCallbackOptions): void
    • Parameters

      • eventName: EventName
      • callback: WatcherCallbackOptions

      Returns void

Variables

IntersectionObserver

IntersectionObserver: IntersectionObserverClass = !!w.IntersectionObserver ? w.IntersectionObserver : SpanielIntersectionObserver

NUM_SKIPPED_FRAMES

NUM_SKIPPED_FRAMES: 3 = 3

TOKEN_SEED

TOKEN_SEED: string = 'xxxx'.replace(/[xy]/g, function(c) {let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);return v.toString(16);})

eventStore

eventStore: EventStore = null

globalEngine

globalEngine: EngineInterface = null

globalScheduler

globalScheduler: Scheduler = null

hasDOM

hasDOM: boolean = !!((typeof window !== 'undefined') && window && (typeof document !== 'undefined') && document)

hasRAF

hasRAF: boolean = hasDOM && !!window.requestAnimationFrame

tokenCounter

tokenCounter: number = 0

Functions

DOMMarginToRootMargin

addRatio

elementSatisfiesRatio

  • elementSatisfiesRatio(el: Element, ratio?: number, callback: function, rootMargin?: DOMMargin): void
  • Parameters

    • el: Element
    • Default value ratio: number = 0
    • callback: function
        • (result: Boolean): void
        • Parameters

          • result: Boolean

          Returns void

    • Default value rootMargin: DOMMargin = { top: 0, bottom: 0, left: 0, right: 0}

    Returns void

entrySatisfiesRatio

  • entrySatisfiesRatio(entry: IntersectionObserverEntry, threshold: number): boolean
  • Parameters

    • entry: IntersectionObserverEntry
    • threshold: number

    Returns boolean

generateEntry

generateToken

  • generateToken(): string

getEventStore

getGlobalEngine

getGlobalScheduler

hasDomSetup

  • hasDomSetup(): void

marginToRect

  • marginToRect(margin: DOMMargin): ClientRect

nop

  • nop(): number

off

  • off(eventName: string, callback: Function): void

on

  • on(eventName: string, callback: function): void

onEntry

queryElement

  • queryElement(el: Element, callback: function): void
  • Parameters

    • el: Element
    • callback: function
        • (bcr: ClientRect, frame: Frame): void
        • Parameters

          • bcr: ClientRect
          • frame: Frame

          Returns void

    Returns void

rootMarginToDOMMargin

scheduleRead

  • scheduleRead(callback: Function): void
  • Schedule a callback to be batched along with other DOM write/mutation work. Use to schedule any DOM changes. Doing so will avoid DOM thrashing.

    Parameters

    • callback: Function

    Returns void

scheduleWork

  • scheduleWork(callback: Function): void
  • Schedule a callback to be batched along with other DOM read/query work. Use to schedule any DOM reads. Doing so will avoid DOM thrashing.

    Parameters

    • callback: Function

    Returns void

setGlobalEngine

trigger

  • trigger(eventName: string, value?: any): void

Object literals

W

W: object

IntersectionObserver

IntersectionObserver: any = hasDOM && (window as any).IntersectionObserver

getHeight

getHeight: nop = nop

getScrollLeft

getScrollLeft: nop = nop

getScrollTop

getScrollTop: nop = nop

getWidth

getWidth: nop = nop

hasDOM

hasDOM: boolean

hasRAF

hasRAF: boolean

rAF

rAF: any = hasRAF ? window.requestAnimationFrame.bind(window) : (callback: Function) => { callback(); }

emptyRect

emptyRect: object

height

height: number = 0

width

width: number = 0

x

x: number = 0

y

y: number = 0

Legend

  • Module
  • Object literal
  • Variable
  • Function
  • Function with type parameter
  • Index signature
  • Type alias
  • Enumeration
  • Enumeration member
  • Property
  • Method
  • Interface
  • Interface with type parameter
  • Constructor
  • Property
  • Method
  • Index signature
  • Class
  • Class with type parameter
  • Constructor
  • Property
  • Method
  • Accessor
  • Index signature
  • Inherited constructor
  • Inherited property
  • Inherited method
  • Inherited accessor
  • Protected property
  • Protected method
  • Protected accessor
  • Private property
  • Private method
  • Private accessor
  • Static property
  • Static method

Generated using TypeDoc