CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tarojs--runtime

Cross-platform runtime for Taro framework providing DOM/BOM polyfills and mini-program bridge functionality

Pending
Overview
Eval results
Files

dom.mddocs/

Document Object Model (DOM) APIs

Complete virtual DOM implementation optimized for mini-program platforms while maintaining web compatibility. The DOM module provides familiar web APIs including nodes, elements, events, and style management.

Overview

The DOM implementation creates a standardized document structure that compiles efficiently to mini-program data structures. Key features include:

  • Virtual Nodes: Complete DOM node hierarchy (Element, Text, Comment)
  • Style Management: CSS properties, class lists, and computed styles
  • Event System: Web-compatible events with mini-program integration
  • Mutation Observation: Change tracking and lifecycle integration
  • Path-based Updates: Efficient rendering with incremental updates
  • Component Aliasing: Platform-specific component mapping

Node Hierarchy

The DOM follows standard web patterns with optimizations for mini-programs:

TaroEventTarget (base)
  ↳ TaroNode (abstract base)
      ↳ TaroElement (general elements)
          ↳ FormElement (form inputs)  
          ↳ SVGElement (SVG elements)
          ↳ TaroRootElement (container root)
      ↳ TaroText (text nodes)

Core Node Classes

TaroNode

import { TaroNode } from '@tarojs/runtime'

abstract class TaroNode extends TaroEventTarget {
  // Identity
  readonly uid: string           // Unique identifier
  readonly sid: string          // Scoped identifier  
  readonly nodeType: number     // Node type constant
  readonly nodeName: string     // Tag name (uppercase)
  
  // Tree structure
  parentNode: TaroNode | null
  childNodes: TaroNode[]
  
  // Computed properties
  get nextSibling(): TaroNode | null
  get previousSibling(): TaroNode | null  
  get parentElement(): TaroElement | null
  get firstChild(): TaroNode | null
  get lastChild(): TaroNode | null
  
  // Path properties (for updates)
  get _path(): string           // Dot-notation path from root
  get _root(): TaroRootElement | null // Root container
  
  // Tree manipulation
  appendChild(child: TaroNode): TaroNode
  insertBefore(newChild: TaroNode, referenceChild: TaroNode | null): TaroNode
  replaceChild(newChild: TaroNode, oldChild: TaroNode): TaroNode  
  removeChild(child: TaroNode): TaroNode
  remove(): void
  
  // Utilities  
  hasChildNodes(): boolean
  enqueueUpdate(): void         // Trigger update batching
}

// Node type constants
const ELEMENT_NODE = 1
const TEXT_NODE = 3  
const COMMENT_NODE = 8
const DOCUMENT_NODE = 9

Key Features

  • Unique Identifiers: Each node gets a unique uid and scoped sid
  • Path Tracking: Automatic path calculation for efficient updates
  • Update Batching: Changes trigger batched updates via enqueueUpdate()
  • Comment Filtering: Comment nodes are filtered from childNodes by default

Usage Examples

// Create node tree
const parent = document.createElement('view')
const child1 = document.createElement('text')  
const child2 = document.createTextNode('Hello')

// Build tree structure
parent.appendChild(child1)
parent.appendChild(child2)

// Navigate tree  
console.log('Parent:', child1.parentNode)        // parent element
console.log('Next sibling:', child1.nextSibling) // child2  
console.log('First child:', parent.firstChild)   // child1
console.log('Has children:', parent.hasChildNodes()) // true

// Get update path
console.log('Node path:', child1._path)          // "root.cn.[0].cn.[0]"
console.log('Root element:', child1._root)       // TaroRootElement

// Tree manipulation
const newChild = document.createElement('image')
parent.insertBefore(newChild, child2)            // Insert between child1 and child2
parent.removeChild(child1)                       // Remove child1
child2.remove()                                  // Remove child2 directly

TaroElement

import { TaroElement } from '@tarojs/runtime'

class TaroElement extends TaroNode {
  // Element properties
  tagName: string               // Tag name (lowercase)
  props: Record<string, any>    // Element properties/attributes
  
  // Style and classes
  style: Style                  // CSS style object
  classList: ClassList          // CSS class management
  dataset: Record<string, string> // Data attributes
  
  // Content properties
  id: string                    // Element ID
  className: string             // CSS classes  
  innerHTML: string             // Inner HTML content
  textContent: string           // Text content
  cssText: string              // Inline styles
  
  // Collections
  children: TaroElement[]       // Child elements (no text nodes)
  attributes: Record<string, string> // All attributes
  
  // Attribute methods
  hasAttribute(name: string): boolean
  hasAttributes(): boolean
  getAttribute(name: string): string | null
  setAttribute(name: string, value: string | number | boolean): void  
  removeAttribute(name: string): void
  
  // Element selection  
  getElementsByTagName(tagName: string): TaroElement[]
  getElementsByClassName(className: string): TaroElement[]
  
  // Focus and interaction
  focus(): void
  blur(): void
  
  // Event handling (inherited from TaroEventTarget)
  addEventListener(type: string, handler: EventListener, options?: AddEventListenerOptions): void
  removeEventListener(type: string, handler: EventListener): void
  dispatchEvent(event: TaroEvent): boolean
}

Key Features

  • Component Aliasing: Automatic mapping to platform-specific components
  • View Optimization: Special handling for pure-view, static-view, catch-view
  • Style Management: Integrated CSS property and class handling
  • Attribute Sync: Automatic synchronization between props and attributes
  • Content Management: Support for both innerHTML and textContent

Usage Examples

// Create and configure element
const view = document.createElement('view')
view.id = 'main-container'
view.className = 'container active'
view.setAttribute('data-test', 'value')

// Style management  
view.style.setProperty('background-color', '#ffffff')
view.style.setProperty('padding', '16px')
view.cssText = 'color: red; font-size: 14px;'

// Class management
view.classList.add('highlighted')
view.classList.remove('active')
view.classList.toggle('visible')

// Content manipulation
view.textContent = 'Hello World'
view.innerHTML = '<text>Formatted <text class="bold">content</text></text>'

// Attribute access
console.log('ID:', view.getAttribute('id'))           // "main-container"
console.log('Has data attr:', view.hasAttribute('data-test')) // true
console.log('All attributes:', view.attributes)       // { id: "main-container", ... }

// Element selection
const textElements = view.getElementsByTagName('text')
const boldElements = view.getElementsByClassName('bold')

// Dataset access (data-* attributes)  
view.setAttribute('data-user-id', '123')
console.log('User ID:', view.dataset.userId)          // "123"

// Event handling
view.addEventListener('tap', (event) => {
  console.log('Element tapped:', event.target)
})

// Focus management
view.focus()  // Set focus to element
view.blur()   // Remove focus

TaroText

import { TaroText } from '@tarojs/runtime'

class TaroText extends TaroNode {
  // Text content
  private _value: string        // Internal text value
  
  // Text properties (all reference _value)
  get textContent(): string
  set textContent(value: string)
  
  get nodeValue(): string  
  set nodeValue(value: string)
  
  get data(): string
  set data(value: string)
  
  // Node type
  nodeType: 3                   // TEXT_NODE constant
  nodeName: '#text'             // Standard text node name
}

Key Features

  • Automatic Updates: Text changes automatically trigger path-based updates
  • Multiple Accessors: Standard web APIs (textContent, nodeValue, data)
  • Mutation Recording: Text modifications are tracked for mini-program updates

Usage Examples

// Create text node
const textNode = document.createTextNode('Initial text')

// Modify content (all equivalent)
textNode.textContent = 'Updated text'
textNode.nodeValue = 'Updated text'  
textNode.data = 'Updated text'

// Access content
console.log('Text:', textNode.textContent)    // "Updated text"
console.log('Value:', textNode.nodeValue)     // "Updated text"
console.log('Data:', textNode.data)          // "Updated text"

// Add to element
const element = document.createElement('text')
element.appendChild(textNode)

// Text content propagates to parent
console.log('Element text:', element.textContent) // "Updated text"

TaroRootElement

import { TaroRootElement } from '@tarojs/runtime'

class TaroRootElement extends TaroElement {
  // Update management
  pendingUpdate: boolean        // Whether update is queued
  updatePayloads: UpdatePayload[] // Queued updates
  
  // Mini-program context  
  ctx: MpInstance | null       // Page/component instance
  
  // Update lifecycle
  enqueueUpdate(): void        // Queue update for next tick
  performUpdate(): void        // Execute pending updates  
  scheduleTask(task: () => void): void // Schedule async task
  
  // Update callbacks
  enqueueUpdateCallback(callback: () => void): void  // Add update callback
  flushUpdateCallback(): void  // Execute all callbacks
  
  // Custom wrapper handling
  customWrapperCache: Map<string, TaroElement>
}

interface UpdatePayload {
  path: string                 // Update path (dot notation)
  value: any                  // New value
}

interface MpInstance {
  setData(data: Record<string, any>, callback?: () => void): void
}

Key Features

  • Update Batching: Collects multiple changes into single setData call
  • Callback Management: Handles post-update callbacks
  • Custom Wrappers: Manages platform-specific wrapper elements
  • Performance Optimization: Minimizes mini-program update frequency

Usage Examples

// Root element is created automatically by document
const root = document.getElementById('app')._root

// Manual update triggering (usually automatic)
root.enqueueUpdate()

// Add update callback
root.enqueueUpdateCallback(() => {
  console.log('Update completed')
})

// Schedule async task  
root.scheduleTask(() => {
  // Task executed after current update cycle
  console.log('Async task executed')  
})

// Access mini-program context
if (root.ctx) {
  root.ctx.setData({ customData: 'value' })
}

Style Management

Style

import { Style } from '@tarojs/runtime'

class Style {
  // Properties
  cssText: string              // Complete CSS text
  
  // Property methods
  setProperty(property: string, value: string | null, priority?: string): void
  removeProperty(property: string): void  
  getPropertyValue(property: string): string
  
  // Direct property access (camelCase)
  [property: string]: string
}

Key Features

  • CSS Property Validation: Validates CSS property names and values
  • CSS Variables: Supports custom properties (--variable-name)
  • Webkit Prefixes: Handles -webkit- prefixed properties
  • CamelCase Conversion: Converts kebab-case to camelCase automatically

Usage Examples

const element = document.createElement('view')

// Set individual properties
element.style.setProperty('background-color', '#ffffff')
element.style.setProperty('font-size', '16px')
element.style.setProperty('--custom-color', '#ff0000')

// Direct property access (camelCase)
element.style.backgroundColor = '#000000'
element.style.fontSize = '18px'
element.style.marginTop = '10px'

// Get property values
console.log('Background:', element.style.getPropertyValue('background-color'))
console.log('Font size:', element.style.fontSize)

// Remove properties
element.style.removeProperty('margin-top')

// Set multiple properties via cssText
element.style.cssText = 'color: red; padding: 8px; border: 1px solid #ccc;'

// Get complete CSS
console.log('All styles:', element.style.cssText)

ClassList

import { ClassList } from '@tarojs/runtime'

class ClassList {
  // Properties
  readonly length: number      // Number of classes
  
  // Methods
  add(...classNames: string[]): void
  remove(...classNames: string[]): void
  toggle(className: string, force?: boolean): boolean
  contains(className: string): boolean
  
  // Iteration
  item(index: number): string | null
  toString(): string
  
  // Array-like access
  [index: number]: string
}

Usage Examples

const element = document.createElement('view')

// Add classes
element.classList.add('container')
element.classList.add('active', 'visible')

// Remove classes  
element.classList.remove('active')

// Toggle classes
element.classList.toggle('hidden')           // Add if not present
element.classList.toggle('visible', false)   // Force remove

// Check for classes
if (element.classList.contains('container')) {
  console.log('Element is a container')
}

// Access by index
console.log('First class:', element.classList[0])     // "container"
console.log('Class count:', element.classList.length) // 2

// Convert to string
console.log('Classes:', element.classList.toString()) // "container visible"

Event System

TaroEvent

import { TaroEvent, createEvent } from '@tarojs/runtime'

interface TaroEvent extends Event {
  // Standard Event properties
  type: string                 // Event type
  bubbles: boolean            // Whether event bubbles
  cancelable: boolean         // Whether event can be canceled
  target: TaroElement         // Event target
  currentTarget: TaroElement  // Current event handler element
  timeStamp: number          // Event timestamp
  
  // Mini-program integration
  mpEvent?: any              // Original mini-program event
  
  // Event control
  stopPropagation(): void
  stopImmediatePropagation(): void  
  preventDefault(): void
  
  // State
  readonly defaultPrevented: boolean
  readonly eventPhase: number
}

// Event creation
function createEvent(event: any, node?: TaroElement): TaroEvent

Key Features

  • Event Bubbling: Simulates web-style event bubbling on mini-programs
  • Dataset Merging: Automatically merges dataset attributes into event
  • Batch Processing: Optimizes event handling with batched updates
  • Cross-platform: Works identically on web and mini-program platforms

Usage Examples

// Event handler
function handleTap(event: TaroEvent) {
  console.log('Event type:', event.type)          // "tap"
  console.log('Target element:', event.target)    // Tapped element
  console.log('Current target:', event.currentTarget) // Handler element
  console.log('Timestamp:', event.timeStamp)      // Event time
  
  // Access mini-program event data
  if (event.mpEvent) {
    console.log('Touch data:', event.mpEvent.touches)
  }
  
  // Control event propagation  
  event.stopPropagation()       // Stop bubbling
  event.preventDefault()        // Cancel default action
}

// Add event listeners
element.addEventListener('tap', handleTap)
element.addEventListener('longpress', (event) => {
  console.log('Long press detected')
})

// Create custom events
const customEvent = createEvent({
  type: 'custom',
  detail: { customData: 'value' }
}, element)

// Dispatch events
element.dispatchEvent(customEvent)

Event Handler

import { eventHandler } from '@tarojs/runtime'

function eventHandler(handler: (event: TaroEvent) => void): (mpEvent: any) => void

The eventHandler function wraps standard event handlers to work with mini-program events:

// Standard handler (receives TaroEvent)
function onTap(event: TaroEvent) {
  console.log('Tapped:', event.target.tagName)
}

// Wrap for mini-program use
const mpHandler = eventHandler(onTap)

// Use in mini-program component
{
  data: {},
  onTap: mpHandler  // Automatically converts mpEvent to TaroEvent
}

EventSource

import { eventSource } from '@tarojs/runtime'

interface EventSource {
  // Global event registry for DOM nodes
  set(node: TaroNode, event: TaroEvent): void
  get(node: TaroNode): TaroEvent | null
  delete(node: TaroNode): void
}

Form Elements

FormElement

import { FormElement } from '@tarojs/runtime'

class FormElement extends TaroElement {
  // Form-specific properties
  type: string                 // Input type
  value: string               // Current value
  
  // Automatic value synchronization on input/change events
}

Usage Examples

// Create form elements
const input = document.createElement('input')
input.type = 'text'
input.value = 'Initial value'

const textarea = document.createElement('textarea')  
textarea.value = 'Multi-line text'

// Value updates automatically sync with mini-program
input.addEventListener('input', (event) => {
  console.log('New value:', input.value)
})

// Form submission
const form = document.createElement('form')
form.appendChild(input)
form.addEventListener('submit', (event) => {
  event.preventDefault()
  console.log('Form data:', input.value)
})

SVG Support

SVGElement

import { SVGElement } from '@tarojs/runtime'

class SVGElement extends TaroElement {
  // SVG-specific implementation
  // Inherits all TaroElement functionality with SVG namespace handling
}

Usage Examples

// Create SVG elements
const svg = document.createElement('svg')
svg.setAttribute('width', '100')
svg.setAttribute('height', '100')

const circle = document.createElement('circle')
circle.setAttribute('cx', '50')  
circle.setAttribute('cy', '50')
circle.setAttribute('r', '25')
circle.setAttribute('fill', 'blue')

svg.appendChild(circle)

Mutation Observer

MutationObserver

import { MutationObserver } from '@tarojs/runtime'

class MutationObserver {
  constructor(callback: MutationCallback)
  
  // Observation control
  observe(target: Node, options?: MutationObserverInit): void
  disconnect(): void
  takeRecords(): MutationRecord[]
}

interface MutationRecord {
  type: 'attributes' | 'characterData' | 'childList'
  target: Node
  addedNodes: NodeList
  removedNodes: NodeList
  previousSibling: Node | null
  nextSibling: Node | null
  attributeName: string | null
  attributeNamespace: string | null  
  oldValue: string | null
}

type MutationCallback = (mutations: MutationRecord[], observer: MutationObserver) => void

Usage Examples

// Create observer
const observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    console.log('Mutation type:', mutation.type)
    console.log('Target:', mutation.target)
    
    if (mutation.type === 'childList') {
      console.log('Added nodes:', mutation.addedNodes.length)
      console.log('Removed nodes:', mutation.removedNodes.length)
    }
    
    if (mutation.type === 'attributes') {
      console.log('Attribute changed:', mutation.attributeName)
    }
  })
})

// Start observing
observer.observe(document.body, {
  childList: true,        // Watch for child additions/removals
  attributes: true,       // Watch for attribute changes
  characterData: true,    // Watch for text content changes
  subtree: true          // Watch entire subtree
})

// Stop observing  
observer.disconnect()

// Get pending mutations
const mutations = observer.takeRecords()

Platform-Specific Optimizations

Component Aliasing

Elements are automatically mapped to platform-specific components:

// Generic element
const view = document.createElement('view')

// Platform-specific mapping:
// Web: <div>
// WeChat: <view>  
// Alipay: <view>
// etc.

View Optimization

Special view types for performance optimization:

// Performance-optimized views
const pureView = document.createElement('pure-view')     // Static content
const staticView = document.createElement('static-view') // No event handling  
const catchView = document.createElement('catch-view')   // Catch touch events
const clickView = document.createElement('click-view')   // Click-only events

Update Optimization

The DOM uses path-based updates for efficient mini-program rendering:

// Automatic path-based updates
const element = document.createElement('view')
element.textContent = 'Hello'  // Updates: { "root.cn.[0].v": "Hello" }

element.style.color = 'red'    // Updates: { "root.cn.[0].st.color": "red" }

The DOM implementation provides complete web compatibility while optimizing for mini-program performance, enabling seamless cross-platform development.

Install with Tessl CLI

npx tessl i tessl/npm-tarojs--runtime

docs

bom.md

dom.md

framework-integration.md

index.md

types.md

utilities.md

tile.json