CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-fastdom

Eliminates layout thrashing by batching DOM read/write operations

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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;
}
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/fastdom@1.0.x
Publish Source
CLI
Badge
tessl/npm-fastdom badge