JavaScript display engine for LaTeX, MathML, and AsciiMath mathematical notation in browsers and Node.js
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
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');
});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]);
}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();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
}
};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;
}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>';
}
};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');
}
}
}
}
}// 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 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);
}
}// 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);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} />;
}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 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();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
};// 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');
}]
}
};// 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');
});