CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-mathjax

JavaScript display engine for LaTeX, MathML, and AsciiMath mathematical notation in browsers and Node.js

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

document-rendering.mddocs/

Document Rendering

MathJax provides comprehensive document rendering capabilities for automatically finding, processing, and rendering mathematical expressions within HTML documents, with support for dynamic content updates and performance optimization.

Capabilities

Document Typesetting

Process mathematical expressions within HTML documents automatically.

/**
 * Typeset mathematics in specified elements (synchronous)
 * @param elements - Elements to process, or null for entire document
 */
function typeset(elements?: Element[]): void;

/**
 * Typeset mathematics in specified elements (asynchronous)
 * @param elements - Elements to process, or null for entire document
 * @returns Promise that resolves when typesetting is complete
 */
function typesetPromise(elements?: Element[]): Promise<void>;

/**
 * Clear typeset mathematics from specified elements
 * @param elements - Elements to clear, or null for entire document
 */
function typesetClear(elements?: Element[]): void;

/**
 * Get completion promise for current operations
 * @returns Promise that resolves when all operations are complete
 */
function done(): Promise<void>;

/**
 * Execute callback when MathJax is ready
 * @param callback - Function to execute when ready
 * @returns Promise that resolves after callback execution
 */
function whenReady(callback: () => void): Promise<void>;

Usage Examples:

// Typeset entire document
MathJax.typeset();

// Typeset specific elements
const elements = document.querySelectorAll('.math-content');
MathJax.typeset(Array.from(elements));

// Asynchronous typesetting
await MathJax.typesetPromise();

// Typeset with callback
MathJax.typesetPromise().then(() => {
  console.log('Typesetting complete');
});

// Clear mathematics from document
MathJax.typesetClear();

// Wait for operations to complete
await MathJax.done();

// Execute when ready
MathJax.whenReady(() => {
  console.log('MathJax is ready');
});

Dynamic Content Updates

Handle dynamically added mathematical content:

// Add new math content
const newContent = document.createElement('div');
newContent.innerHTML = 'The equation is $x^2 + y^2 = z^2$.';
document.body.appendChild(newContent);

// Typeset only the new content
MathJax.typeset([newContent]);

// Or typeset the entire document
MathJax.typeset();

// Handle content updates with promises
async function updateMathContent() {
  // Clear existing math
  MathJax.typesetClear([targetElement]);
  
  // Update content
  targetElement.innerHTML = newMathExpression;
  
  // Re-typeset
  await MathJax.typesetPromise([targetElement]);
}

Document Processing Control

Control which elements and content get processed:

// Configure processing classes
MathJax.config.options = {
  processHtmlClass: 'tex2jax_process',
  ignoreHtmlClass: 'tex2jax_ignore',
  processScriptType: 'math/tex'
};

// Process only specific elements
const mathElements = document.querySelectorAll('.contains-math');
MathJax.typeset(Array.from(mathElements));

// Skip specific elements
const elementsToSkip = document.querySelectorAll('.skip-math');
elementsToSkip.forEach(el => el.classList.add('tex2jax_ignore'));
MathJax.typeset();

Document Configuration

Startup Options

interface StartupOptions {
  /** Elements to process on startup */
  elements?: Element[] | null;
  /** Auto-typeset on ready */
  typeset?: boolean;
  /** Ready callback function */
  ready?: () => void;
  /** Page ready callback function */
  pageReady?: () => void;
  /** Document or document selector */
  document?: string | Document;
  /** Input processors to use */
  input?: string[];
  /** Output processor to use */
  output?: string;
  /** DOM adaptor to use */
  adaptor?: string | null;
  /** Document handler to use */
  handler?: string | null;
}

Configuration Examples:

// Browser configuration
window.MathJax = {
  startup: {
    elements: null,  // Process entire document
    typeset: true,   // Auto-typeset on load
    ready: () => {
      console.log('MathJax startup complete');
    },
    pageReady: () => {
      console.log('Page ready for typesetting');
    }
  }
};

// Node.js configuration
const config = {
  startup: {
    typeset: false,  // Don't auto-typeset in Node.js
    document: myDocument,
    elements: specificElements
  }
};

Processing Options

interface ProcessingOptions {
  /** Elements to process */
  elements?: Element[];
  /** HTML class for processing */
  processHtmlClass?: string;
  /** HTML class to ignore */
  ignoreHtmlClass?: string;
  /** Script type to process */
  processScriptType?: string;
  /** Enable assistive MathML */
  enableAssistiveMml?: boolean;
  /** Compile error handler */
  compileError?: (doc: MathDocument, math: MathItem, error: Error) => void;
  /** Typeset error handler */
  typesetError?: (doc: MathDocument, math: MathItem, error: Error) => void;
}

Error Handling

Processing Errors

Handle errors during document processing:

// Configure error handlers
MathJax.config.options = {
  compileError: (doc, math, error) => {
    console.error('Compilation error:', error.message);
    // Display error message in place of math
    math.typesetRoot.innerHTML = `<span style="color:red">[${error.message}]</span>`;
  },
  
  typesetError: (doc, math, error) => {
    console.error('Typesetting error:', error.message);
    // Handle typesetting failure
    math.typesetRoot.innerHTML = '<span style="color:red">[Typeset Error]</span>';
  }
};

Graceful Error Recovery

async function safeTypeset(elements) {
  try {
    await MathJax.typesetPromise(elements);
  } catch (error) {
    console.error('Typesetting failed:', error.message);
    
    // Fallback: try individual elements
    if (elements) {
      for (const element of elements) {
        try {
          await MathJax.typesetPromise([element]);
        } catch (elementError) {
          console.warn(`Failed to typeset element:`, elementError.message);
          // Mark element with error class
          element.classList.add('math-error');
        }
      }
    }
  }
}

Performance Optimization

Efficient Document Processing

// Process only changed content
class MathContentManager {
  constructor() {
    this.processedElements = new WeakSet();
  }
  
  async updateElement(element) {
    // Clear if previously processed
    if (this.processedElements.has(element)) {
      MathJax.typesetClear([element]);
    }
    
    // Process element
    await MathJax.typesetPromise([element]);
    this.processedElements.add(element);
  }
  
  async updateMultiple(elements) {
    // Clear all elements first
    const toProcess = elements.filter(el => this.processedElements.has(el));
    if (toProcess.length > 0) {
      MathJax.typesetClear(toProcess);
    }
    
    // Process all elements
    await MathJax.typesetPromise(elements);
    elements.forEach(el => this.processedElements.add(el));
  }
}

Batch Processing

// Batch multiple updates
class BatchProcessor {
  constructor() {
    this.pendingElements = new Set();
    this.timeoutId = null;
  }
  
  scheduleUpdate(element) {
    this.pendingElements.add(element);
    
    // Debounce updates
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
    }
    
    this.timeoutId = setTimeout(() => {
      this.processBatch();
    }, 100);
  }
  
  async processBatch() {
    if (this.pendingElements.size === 0) return;
    
    const elements = Array.from(this.pendingElements);
    this.pendingElements.clear();
    
    await MathJax.typesetPromise(elements);
  }
}

Advanced Document Processing

Custom Math Discovery

// Custom math finder
function findCustomMath(container) {
  const mathElements = [];
  
  // Find custom math delimiters
  const walker = document.createTreeWalker(
    container,
    NodeFilter.SHOW_TEXT,
    null,
    false
  );
  
  let node;
  while (node = walker.nextNode()) {
    if (node.textContent.includes('{{math}}')) {
      // Process custom math syntax
      mathElements.push(node.parentElement);
    }
  }
  
  return mathElements;
}

// Process custom math
const customMath = findCustomMath(document.body);
MathJax.typeset(customMath);

Integration with Frameworks

React Integration

import { useEffect, useRef } from 'react';

function MathComponent({ expression, display = false }) {
  const mathRef = useRef();
  
  useEffect(() => {
    if (mathRef.current && window.MathJax) {
      // Clear previous content
      MathJax.typesetClear([mathRef.current]);
      
      // Set new content
      mathRef.current.innerHTML = display 
        ? `$$${expression}$$` 
        : `$${expression}$`;
      
      // Typeset new content
      MathJax.typeset([mathRef.current]);
    }
  }, [expression, display]);
  
  return <div ref={mathRef} />;
}

Vue Integration

export default {
  props: ['expression', 'display'],
  mounted() {
    this.renderMath();
  },
  updated() {
    this.renderMath();
  },
  methods: {
    async renderMath() {
      if (this.$refs.math && window.MathJax) {
        MathJax.typesetClear([this.$refs.math]);
        
        const delimiter = this.display ? '$$' : '$';
        this.$refs.math.innerHTML = `${delimiter}${this.expression}${delimiter}`;
        
        await MathJax.typesetPromise([this.$refs.math]);
      }
    }
  },
  template: '<div ref="math"></div>'
};

Progressive Enhancement

// Progressive enhancement for math content
class MathEnhancer {
  constructor() {
    this.observer = new MutationObserver(this.handleMutations.bind(this));
  }
  
  start() {
    // Process existing content
    MathJax.typeset();
    
    // Watch for new content
    this.observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  }
  
  stop() {
    this.observer.disconnect();
  }
  
  handleMutations(mutations) {
    const elementsToProcess = new Set();
    
    mutations.forEach(mutation => {
      mutation.addedNodes.forEach(node => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          // Check if element contains math
          if (this.containsMath(node)) {
            elementsToProcess.add(node);
          }
          
          // Check child elements
          const mathElements = node.querySelectorAll('.math, .tex');
          mathElements.forEach(el => elementsToProcess.add(el));
        }
      });
    });
    
    if (elementsToProcess.size > 0) {
      MathJax.typeset(Array.from(elementsToProcess));
    }
  }
  
  containsMath(element) {
    const text = element.textContent || '';
    return text.includes('$') || text.includes('\\(') || text.includes('\\[');
  }
}

// Start progressive enhancement
const enhancer = new MathEnhancer();
enhancer.start();

Document States and Lifecycle

Processing States

MathJax processes mathematics through several states:

const STATE = {
  UNPROCESSED: 0,    // Initial state
  FINDMATH: 10,      // Finding math in document
  COMPILED: 20,      // Math compiled to internal format
  CONVERT: 100,      // Converting to output format
  METRICS: 110,      // Computing metrics
  RERENDER: 125,     // Re-rendering if needed
  TYPESET: 150,      // Typesetting complete
  INSERTED: 200,     // Inserted into document
  LAST: 10000        // Final state
};

Lifecycle Hooks

// Hook into processing lifecycle
MathJax.config.options = {
  renderActions: {
    findScript: [150, (doc) => {
      // Custom script finding logic
      console.log('Finding scripts in document');
    }],
    
    insertedScript: [200, (doc) => {
      // After insertion callback
      console.log('Math inserted into document');
    }]
  }
};

Document Ready Events

// Wait for document ready
MathJax.whenReady(() => {
  console.log('MathJax is ready');
  
  // Safe to perform operations
  MathJax.typeset();
});

// Promise-based ready handling
const mathReady = MathJax.done();
mathReady.then(() => {
  console.log('All math processing complete');
});

// Chained operations
MathJax.typesetPromise()
  .then(() => MathJax.done())
  .then(() => {
    console.log('Document fully processed');
  });

docs

accessibility.md

document-rendering.md

extensions.md

index.md

initialization.md

input-processing.md

output-formats.md

tile.json