or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-fastdom

Eliminates layout thrashing by batching DOM read/write operations

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/fastdom@1.0.x

To install, run

npx @tessl/cli install tessl/npm-fastdom@1.0.0

index.mddocs/

FastDOM

FastDOM eliminates layout thrashing by batching DOM read and write operations to prevent unnecessary document reflows. It provides a simple API that ensures DOM measurements and mutations happen at optimal times during the browser's rendering cycle, resulting in smoother animations and better performance.

Package Information

  • Package Name: fastdom
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install fastdom

Core Imports

import fastdom from "fastdom";

For CommonJS:

const fastdom = require("fastdom");

For browser (global):

<script src="fastdom.js"></script>
<!-- fastdom is available as window.fastdom -->

Basic Usage

import fastdom from "fastdom";

// Schedule DOM reads (measurements) first
fastdom.measure(() => {
  const width = element.clientWidth;
  const height = element.clientHeight;
  
  // Schedule DOM writes (mutations) based on measurements
  fastdom.mutate(() => {
    element.style.width = width * 2 + 'px';
    element.style.height = height * 2 + 'px';
  });
});

// Multiple operations are batched automatically
fastdom.measure(() => console.log('measure 1'));
fastdom.measure(() => console.log('measure 2'));
fastdom.mutate(() => console.log('mutate 1'));
fastdom.mutate(() => console.log('mutate 2'));
// Output: measure 1, measure 2, mutate 1, mutate 2

Architecture

FastDOM operates as a singleton across all modules in your application, providing a regulatory layer between your code and the DOM:

  • Batching System: Queues DOM operations and executes them in optimal order (reads first, then writes)
  • Request Animation Frame: Uses requestAnimationFrame to time operations with the browser's rendering cycle
  • Task Scheduling: Maintains separate queues for measure (read) and mutate (write) operations
  • Extension System: Supports extensions that add functionality while using the same global task queue
  • Error Handling: Provides configurable error handling for failed operations

Capabilities

DOM Measurement Operations

Schedule DOM read operations that won't cause layout thrashing.

/**
 * Schedules a job for the 'measure' queue. Returns a unique ID that can be used to clear the scheduled job.
 * @param {Function} fn - Callback function to execute during read phase
 * @param {Object} [ctx] - Optional context to bind to the function
 * @returns {Function} The scheduled task that can be passed to clear()
 */
function measure(fn: () => void, ctx?: any): () => void;

Usage Examples:

// Basic measurement
const task = fastdom.measure(() => {
  const rect = element.getBoundingClientRect();
  console.log(rect.width, rect.height);
});

// With context binding
class Component {
  updateSize() {
    fastdom.measure(this.measureElement, this);
  }
  
  measureElement() {
    this.width = this.element.clientWidth;
  }
}

DOM Mutation Operations

Schedule DOM write operations to prevent layout thrashing.

/**
 * Schedules a job for the 'mutate' queue. Returns a unique ID that can be used to clear the scheduled job.
 * @param {Function} fn - Callback function to execute during write phase
 * @param {Object} [ctx] - Optional context to bind to the function
 * @returns {Function} The scheduled task that can be passed to clear()
 */
function mutate(fn: () => void, ctx?: any): () => void;

Usage Examples:

// Basic mutation
const task = fastdom.mutate(() => {
  element.style.transform = 'translateX(100px)';
  element.classList.add('moved');
});

// Chaining measure and mutate
fastdom.measure(() => {
  const width = element.clientWidth;
  
  fastdom.mutate(() => {
    element.style.width = (width * 1.5) + 'px';
  });
});

Task Cancellation

Clear scheduled tasks before they execute.

/**
 * Clears any scheduled job.
 * @param {Function} task - The task returned from measure() or mutate()
 * @returns {boolean} True if task was successfully cleared
 */
function clear(task: () => void): boolean;

Usage Examples:

const readTask = fastdom.measure(() => {
  console.log('This will be cancelled');
});

const writeTask = fastdom.mutate(() => {
  console.log('This will also be cancelled');
});

// Cancel the tasks before they execute
fastdom.clear(readTask);   // returns true
fastdom.clear(writeTask);  // returns true

Extension System

Create extended FastDOM instances with additional functionality.

/**
 * Extend this FastDom with custom functionality.
 * @param {Object} props - Properties to mixin to create extended instance
 * @returns {FastDom} New extended FastDom instance
 */
function extend(props: object): FastDom;

Usage Examples:

const myFastdom = fastdom.extend({
  initialize() {
    console.log('Extension initialized');
  },
  
  // Override existing methods
  measure(fn) {
    console.log('Custom measure logic');
    return this.fastdom.measure(fn);
  },
  
  // Add new methods
  batch(operations) {
    operations.forEach(op => {
      if (op.type === 'measure') {
        this.measure(op.fn);
      } else {
        this.mutate(op.fn);
      }
    });
  }
});

myFastdom.batch([
  { type: 'measure', fn: () => console.log('measure') },
  { type: 'mutate', fn: () => console.log('mutate') }
]);

Error Handling

Configure custom error handling for task failures.

/**
 * Error handler property for tasks that throw exceptions
 * @type {Function|null}
 */
let catch: ((error: Error) => void) | null;

Usage Examples:

// Set global error handler
fastdom.catch = (error) => {
  console.error('FastDOM task failed:', error);
  // Send to error reporting service
  errorReporting.report(error);
};

// Tasks that error will now be handled gracefully
fastdom.measure(() => {
  throw new Error('Something went wrong');
  // This error will be caught and handled by the catch function
});

Extensions

Promise-Based API (fastdom-promised)

Adds Promise-based API for improved control flow.

<script src="fastdom.js"></script>
<script src="extensions/fastdom-promised.js"></script>
// For CommonJS/ES modules
const fastdomPromised = require('fastdom-promised');
// or: import fastdomPromised from 'fastdom-promised';

// For browser global (after loading script)
// fastdomPromised is available as window.fastdomPromised

// Extend fastdom with promise functionality
const promisedFastdom = fastdom.extend(fastdomPromised);

/**
 * Promise-based measure operation
 * @param {Function} fn - Task function that can return a value
 * @param {Object} [ctx] - Optional execution context
 * @returns {Promise} Promise that resolves with the function's return value
 */
function measure(fn: () => any, ctx?: any): Promise<any>;

/**
 * Promise-based mutate operation  
 * @param {Function} fn - Task function that can return a value
 * @param {Object} [ctx] - Optional execution context
 * @returns {Promise} Promise that resolves with the function's return value
 */
function mutate(fn: () => any, ctx?: any): Promise<any>;

/**
 * Clear a promise-based task
 * @param {Promise} promise - The promise returned from measure/mutate
 */
function clear(promise: Promise<any>): void;

Usage Examples:

// Promise-based API
const width = await promisedFastdom.measure(() => element.clientWidth);

await promisedFastdom.mutate(() => {
  element.style.width = width * 2 + 'px';
});

// Chaining promises
promisedFastdom.measure(() => element.clientWidth)
  .then(width => promisedFastdom.mutate(() => {
    element.style.width = width * 2 + 'px';
  }))
  .then(() => console.log('Done!'));

// Clearing promise-based tasks
const promise = promisedFastdom.measure(() => {
  return element.clientWidth;
});

promisedFastdom.clear(promise);

Sandbox System (fastdom-sandbox)

Provides task grouping for component-based development.

<script src="fastdom.js"></script>
<script src="extensions/fastdom-sandbox.js"></script>
// For CommonJS/ES modules
const fastdomSandbox = require('fastdom-sandbox');
// or: import fastdomSandbox from 'fastdom-sandbox';

// For browser global (after loading script)
// fastdomSandbox is available as window.fastdomSandbox

// Extend fastdom with sandbox functionality
const sandboxedFastdom = fastdom.extend(fastdomSandbox);

/**
 * Create a new sandbox for grouping tasks
 * @returns {Sandbox} New sandbox instance
 */
function sandbox(): Sandbox;

interface Sandbox {
  /**
   * Schedule measure task in sandbox
   * @param {Function} fn - Task function
   * @param {Object} [ctx] - Optional execution context
   * @returns {Function} The scheduled task
   */
  measure(fn: () => void, ctx?: any): () => void;
  
  /**
   * Schedule mutate task in sandbox
   * @param {Function} fn - Task function
   * @param {Object} [ctx] - Optional execution context
   * @returns {Function} The scheduled task
   */
  mutate(fn: () => void, ctx?: any): () => void;
  
  /**
   * Clear single task or all tasks in sandbox
   * @param {Function} [task] - Specific task to clear, or clear all if omitted
   * @returns {boolean} Result from fastdom.clear()
   */
  clear(task?: () => void): boolean;
}

Usage Examples:

// Create sandbox for component
class Component {
  constructor() {
    this.sandbox = sandboxedFastdom.sandbox();
  }
  
  updateLayout() {
    this.sandbox.measure(() => {
      this.width = this.element.clientWidth;
    });
    
    this.sandbox.mutate(() => {
      this.element.style.width = this.width * 2 + 'px';
    });
  }
  
  destroy() {
    // Clear all tasks for this component
    this.sandbox.clear();
  }
}

// Manual sandbox usage
const sandbox = sandboxedFastdom.sandbox();

sandbox.measure(() => console.log('measure 1'));
sandbox.measure(() => console.log('measure 2'));
sandbox.mutate(() => console.log('mutate 1'));

// Clear all tasks in the sandbox
sandbox.clear();

Strict Mode (fastdom-strict)

Development-time enforcement of proper DOM access patterns.

<script src="fastdom.js"></script>
<script src="fastdom-strict.js"></script>

Note: When fastdom-strict.js is loaded, it extends the global fastdom instance with strict mode functionality. The strict() method becomes available on the fastdom instance.

Usage Examples:

// Strict mode is enabled by default when fastdom-strict.js is loaded
// This will throw an error:
element.clientWidth; // Error: Can only get .clientWidth during 'measure' phase

// This will throw an error:
fastdom.mutate(() => {
  element.clientWidth; // Error: Can only get .clientWidth during 'measure' phase
});

// This works correctly:
fastdom.measure(() => {
  element.clientWidth; // No error
});

// Disable strict mode using the method on the fastdom instance
fastdom.strict(false);
element.clientWidth; // No error when disabled

// Re-enable strict mode
fastdom.strict(true);

Types

// Core FastDom interface
interface FastDom {
  measure<T extends () => void>(fn: T, ctx?: any): T;
  mutate<T extends () => void>(fn: T, ctx?: any): T;
  clear<T extends () => void>(task: T): boolean;
  extend<T extends object>(props: T): Omit<FastDom, keyof T & keyof FastDom> & T;
  catch: null | ((e: unknown) => any);
  // Available when fastdom-strict.js is loaded
  strict?(value: boolean): void;
}

// FastDOM Promised extension types
interface FastdomPromised {
  clear<T extends Promise<any>>(task: T): void;
  initialize(): void;
  measure<T extends () => void>(fn: T, ctx?: any): Promise<ReturnType<T>>;
  mutate<T extends () => void>(fn: T, ctx?: any): Promise<ReturnType<T>>;
}

// Sandbox interface
interface Sandbox {
  fastdom: FastDom;
  tasks: (() => void)[];
  measure(fn: () => void, ctx?: any): () => void;
  mutate(fn: () => void, ctx?: any): () => void;
  clear(task?: () => void): boolean;
}