0
# Document Object Model (DOM) APIs
1
2
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.
3
4
## Overview
5
6
The DOM implementation creates a standardized document structure that compiles efficiently to mini-program data structures. Key features include:
7
8
- **Virtual Nodes**: Complete DOM node hierarchy (Element, Text, Comment)
9
- **Style Management**: CSS properties, class lists, and computed styles
10
- **Event System**: Web-compatible events with mini-program integration
11
- **Mutation Observation**: Change tracking and lifecycle integration
12
- **Path-based Updates**: Efficient rendering with incremental updates
13
- **Component Aliasing**: Platform-specific component mapping
14
15
## Node Hierarchy
16
17
The DOM follows standard web patterns with optimizations for mini-programs:
18
19
```
20
TaroEventTarget (base)
21
↳ TaroNode (abstract base)
22
↳ TaroElement (general elements)
23
↳ FormElement (form inputs)
24
↳ SVGElement (SVG elements)
25
↳ TaroRootElement (container root)
26
↳ TaroText (text nodes)
27
```
28
29
## Core Node Classes
30
31
### TaroNode
32
33
```typescript { .api }
34
import { TaroNode } from '@tarojs/runtime'
35
36
abstract class TaroNode extends TaroEventTarget {
37
// Identity
38
readonly uid: string // Unique identifier
39
readonly sid: string // Scoped identifier
40
readonly nodeType: number // Node type constant
41
readonly nodeName: string // Tag name (uppercase)
42
43
// Tree structure
44
parentNode: TaroNode | null
45
childNodes: TaroNode[]
46
47
// Computed properties
48
get nextSibling(): TaroNode | null
49
get previousSibling(): TaroNode | null
50
get parentElement(): TaroElement | null
51
get firstChild(): TaroNode | null
52
get lastChild(): TaroNode | null
53
54
// Path properties (for updates)
55
get _path(): string // Dot-notation path from root
56
get _root(): TaroRootElement | null // Root container
57
58
// Tree manipulation
59
appendChild(child: TaroNode): TaroNode
60
insertBefore(newChild: TaroNode, referenceChild: TaroNode | null): TaroNode
61
replaceChild(newChild: TaroNode, oldChild: TaroNode): TaroNode
62
removeChild(child: TaroNode): TaroNode
63
remove(): void
64
65
// Utilities
66
hasChildNodes(): boolean
67
enqueueUpdate(): void // Trigger update batching
68
}
69
70
// Node type constants
71
const ELEMENT_NODE = 1
72
const TEXT_NODE = 3
73
const COMMENT_NODE = 8
74
const DOCUMENT_NODE = 9
75
```
76
77
#### Key Features
78
79
- **Unique Identifiers**: Each node gets a unique `uid` and scoped `sid`
80
- **Path Tracking**: Automatic path calculation for efficient updates
81
- **Update Batching**: Changes trigger batched updates via `enqueueUpdate()`
82
- **Comment Filtering**: Comment nodes are filtered from `childNodes` by default
83
84
#### Usage Examples
85
86
```typescript
87
// Create node tree
88
const parent = document.createElement('view')
89
const child1 = document.createElement('text')
90
const child2 = document.createTextNode('Hello')
91
92
// Build tree structure
93
parent.appendChild(child1)
94
parent.appendChild(child2)
95
96
// Navigate tree
97
console.log('Parent:', child1.parentNode) // parent element
98
console.log('Next sibling:', child1.nextSibling) // child2
99
console.log('First child:', parent.firstChild) // child1
100
console.log('Has children:', parent.hasChildNodes()) // true
101
102
// Get update path
103
console.log('Node path:', child1._path) // "root.cn.[0].cn.[0]"
104
console.log('Root element:', child1._root) // TaroRootElement
105
106
// Tree manipulation
107
const newChild = document.createElement('image')
108
parent.insertBefore(newChild, child2) // Insert between child1 and child2
109
parent.removeChild(child1) // Remove child1
110
child2.remove() // Remove child2 directly
111
```
112
113
### TaroElement
114
115
```typescript { .api }
116
import { TaroElement } from '@tarojs/runtime'
117
118
class TaroElement extends TaroNode {
119
// Element properties
120
tagName: string // Tag name (lowercase)
121
props: Record<string, any> // Element properties/attributes
122
123
// Style and classes
124
style: Style // CSS style object
125
classList: ClassList // CSS class management
126
dataset: Record<string, string> // Data attributes
127
128
// Content properties
129
id: string // Element ID
130
className: string // CSS classes
131
innerHTML: string // Inner HTML content
132
textContent: string // Text content
133
cssText: string // Inline styles
134
135
// Collections
136
children: TaroElement[] // Child elements (no text nodes)
137
attributes: Record<string, string> // All attributes
138
139
// Attribute methods
140
hasAttribute(name: string): boolean
141
hasAttributes(): boolean
142
getAttribute(name: string): string | null
143
setAttribute(name: string, value: string | number | boolean): void
144
removeAttribute(name: string): void
145
146
// Element selection
147
getElementsByTagName(tagName: string): TaroElement[]
148
getElementsByClassName(className: string): TaroElement[]
149
150
// Focus and interaction
151
focus(): void
152
blur(): void
153
154
// Event handling (inherited from TaroEventTarget)
155
addEventListener(type: string, handler: EventListener, options?: AddEventListenerOptions): void
156
removeEventListener(type: string, handler: EventListener): void
157
dispatchEvent(event: TaroEvent): boolean
158
}
159
```
160
161
#### Key Features
162
163
- **Component Aliasing**: Automatic mapping to platform-specific components
164
- **View Optimization**: Special handling for `pure-view`, `static-view`, `catch-view`
165
- **Style Management**: Integrated CSS property and class handling
166
- **Attribute Sync**: Automatic synchronization between props and attributes
167
- **Content Management**: Support for both `innerHTML` and `textContent`
168
169
#### Usage Examples
170
171
```typescript
172
// Create and configure element
173
const view = document.createElement('view')
174
view.id = 'main-container'
175
view.className = 'container active'
176
view.setAttribute('data-test', 'value')
177
178
// Style management
179
view.style.setProperty('background-color', '#ffffff')
180
view.style.setProperty('padding', '16px')
181
view.cssText = 'color: red; font-size: 14px;'
182
183
// Class management
184
view.classList.add('highlighted')
185
view.classList.remove('active')
186
view.classList.toggle('visible')
187
188
// Content manipulation
189
view.textContent = 'Hello World'
190
view.innerHTML = '<text>Formatted <text class="bold">content</text></text>'
191
192
// Attribute access
193
console.log('ID:', view.getAttribute('id')) // "main-container"
194
console.log('Has data attr:', view.hasAttribute('data-test')) // true
195
console.log('All attributes:', view.attributes) // { id: "main-container", ... }
196
197
// Element selection
198
const textElements = view.getElementsByTagName('text')
199
const boldElements = view.getElementsByClassName('bold')
200
201
// Dataset access (data-* attributes)
202
view.setAttribute('data-user-id', '123')
203
console.log('User ID:', view.dataset.userId) // "123"
204
205
// Event handling
206
view.addEventListener('tap', (event) => {
207
console.log('Element tapped:', event.target)
208
})
209
210
// Focus management
211
view.focus() // Set focus to element
212
view.blur() // Remove focus
213
```
214
215
### TaroText
216
217
```typescript { .api }
218
import { TaroText } from '@tarojs/runtime'
219
220
class TaroText extends TaroNode {
221
// Text content
222
private _value: string // Internal text value
223
224
// Text properties (all reference _value)
225
get textContent(): string
226
set textContent(value: string)
227
228
get nodeValue(): string
229
set nodeValue(value: string)
230
231
get data(): string
232
set data(value: string)
233
234
// Node type
235
nodeType: 3 // TEXT_NODE constant
236
nodeName: '#text' // Standard text node name
237
}
238
```
239
240
#### Key Features
241
242
- **Automatic Updates**: Text changes automatically trigger path-based updates
243
- **Multiple Accessors**: Standard web APIs (`textContent`, `nodeValue`, `data`)
244
- **Mutation Recording**: Text modifications are tracked for mini-program updates
245
246
#### Usage Examples
247
248
```typescript
249
// Create text node
250
const textNode = document.createTextNode('Initial text')
251
252
// Modify content (all equivalent)
253
textNode.textContent = 'Updated text'
254
textNode.nodeValue = 'Updated text'
255
textNode.data = 'Updated text'
256
257
// Access content
258
console.log('Text:', textNode.textContent) // "Updated text"
259
console.log('Value:', textNode.nodeValue) // "Updated text"
260
console.log('Data:', textNode.data) // "Updated text"
261
262
// Add to element
263
const element = document.createElement('text')
264
element.appendChild(textNode)
265
266
// Text content propagates to parent
267
console.log('Element text:', element.textContent) // "Updated text"
268
```
269
270
### TaroRootElement
271
272
```typescript { .api }
273
import { TaroRootElement } from '@tarojs/runtime'
274
275
class TaroRootElement extends TaroElement {
276
// Update management
277
pendingUpdate: boolean // Whether update is queued
278
updatePayloads: UpdatePayload[] // Queued updates
279
280
// Mini-program context
281
ctx: MpInstance | null // Page/component instance
282
283
// Update lifecycle
284
enqueueUpdate(): void // Queue update for next tick
285
performUpdate(): void // Execute pending updates
286
scheduleTask(task: () => void): void // Schedule async task
287
288
// Update callbacks
289
enqueueUpdateCallback(callback: () => void): void // Add update callback
290
flushUpdateCallback(): void // Execute all callbacks
291
292
// Custom wrapper handling
293
customWrapperCache: Map<string, TaroElement>
294
}
295
296
interface UpdatePayload {
297
path: string // Update path (dot notation)
298
value: any // New value
299
}
300
301
interface MpInstance {
302
setData(data: Record<string, any>, callback?: () => void): void
303
}
304
```
305
306
#### Key Features
307
308
- **Update Batching**: Collects multiple changes into single `setData` call
309
- **Callback Management**: Handles post-update callbacks
310
- **Custom Wrappers**: Manages platform-specific wrapper elements
311
- **Performance Optimization**: Minimizes mini-program update frequency
312
313
#### Usage Examples
314
315
```typescript
316
// Root element is created automatically by document
317
const root = document.getElementById('app')._root
318
319
// Manual update triggering (usually automatic)
320
root.enqueueUpdate()
321
322
// Add update callback
323
root.enqueueUpdateCallback(() => {
324
console.log('Update completed')
325
})
326
327
// Schedule async task
328
root.scheduleTask(() => {
329
// Task executed after current update cycle
330
console.log('Async task executed')
331
})
332
333
// Access mini-program context
334
if (root.ctx) {
335
root.ctx.setData({ customData: 'value' })
336
}
337
```
338
339
## Style Management
340
341
### Style
342
343
```typescript { .api }
344
import { Style } from '@tarojs/runtime'
345
346
class Style {
347
// Properties
348
cssText: string // Complete CSS text
349
350
// Property methods
351
setProperty(property: string, value: string | null, priority?: string): void
352
removeProperty(property: string): void
353
getPropertyValue(property: string): string
354
355
// Direct property access (camelCase)
356
[property: string]: string
357
}
358
```
359
360
#### Key Features
361
362
- **CSS Property Validation**: Validates CSS property names and values
363
- **CSS Variables**: Supports custom properties (--variable-name)
364
- **Webkit Prefixes**: Handles -webkit- prefixed properties
365
- **CamelCase Conversion**: Converts kebab-case to camelCase automatically
366
367
#### Usage Examples
368
369
```typescript
370
const element = document.createElement('view')
371
372
// Set individual properties
373
element.style.setProperty('background-color', '#ffffff')
374
element.style.setProperty('font-size', '16px')
375
element.style.setProperty('--custom-color', '#ff0000')
376
377
// Direct property access (camelCase)
378
element.style.backgroundColor = '#000000'
379
element.style.fontSize = '18px'
380
element.style.marginTop = '10px'
381
382
// Get property values
383
console.log('Background:', element.style.getPropertyValue('background-color'))
384
console.log('Font size:', element.style.fontSize)
385
386
// Remove properties
387
element.style.removeProperty('margin-top')
388
389
// Set multiple properties via cssText
390
element.style.cssText = 'color: red; padding: 8px; border: 1px solid #ccc;'
391
392
// Get complete CSS
393
console.log('All styles:', element.style.cssText)
394
```
395
396
### ClassList
397
398
```typescript { .api }
399
import { ClassList } from '@tarojs/runtime'
400
401
class ClassList {
402
// Properties
403
readonly length: number // Number of classes
404
405
// Methods
406
add(...classNames: string[]): void
407
remove(...classNames: string[]): void
408
toggle(className: string, force?: boolean): boolean
409
contains(className: string): boolean
410
411
// Iteration
412
item(index: number): string | null
413
toString(): string
414
415
// Array-like access
416
[index: number]: string
417
}
418
```
419
420
#### Usage Examples
421
422
```typescript
423
const element = document.createElement('view')
424
425
// Add classes
426
element.classList.add('container')
427
element.classList.add('active', 'visible')
428
429
// Remove classes
430
element.classList.remove('active')
431
432
// Toggle classes
433
element.classList.toggle('hidden') // Add if not present
434
element.classList.toggle('visible', false) // Force remove
435
436
// Check for classes
437
if (element.classList.contains('container')) {
438
console.log('Element is a container')
439
}
440
441
// Access by index
442
console.log('First class:', element.classList[0]) // "container"
443
console.log('Class count:', element.classList.length) // 2
444
445
// Convert to string
446
console.log('Classes:', element.classList.toString()) // "container visible"
447
```
448
449
## Event System
450
451
### TaroEvent
452
453
```typescript { .api }
454
import { TaroEvent, createEvent } from '@tarojs/runtime'
455
456
interface TaroEvent extends Event {
457
// Standard Event properties
458
type: string // Event type
459
bubbles: boolean // Whether event bubbles
460
cancelable: boolean // Whether event can be canceled
461
target: TaroElement // Event target
462
currentTarget: TaroElement // Current event handler element
463
timeStamp: number // Event timestamp
464
465
// Mini-program integration
466
mpEvent?: any // Original mini-program event
467
468
// Event control
469
stopPropagation(): void
470
stopImmediatePropagation(): void
471
preventDefault(): void
472
473
// State
474
readonly defaultPrevented: boolean
475
readonly eventPhase: number
476
}
477
478
// Event creation
479
function createEvent(event: any, node?: TaroElement): TaroEvent
480
```
481
482
#### Key Features
483
484
- **Event Bubbling**: Simulates web-style event bubbling on mini-programs
485
- **Dataset Merging**: Automatically merges `dataset` attributes into event
486
- **Batch Processing**: Optimizes event handling with batched updates
487
- **Cross-platform**: Works identically on web and mini-program platforms
488
489
#### Usage Examples
490
491
```typescript
492
// Event handler
493
function handleTap(event: TaroEvent) {
494
console.log('Event type:', event.type) // "tap"
495
console.log('Target element:', event.target) // Tapped element
496
console.log('Current target:', event.currentTarget) // Handler element
497
console.log('Timestamp:', event.timeStamp) // Event time
498
499
// Access mini-program event data
500
if (event.mpEvent) {
501
console.log('Touch data:', event.mpEvent.touches)
502
}
503
504
// Control event propagation
505
event.stopPropagation() // Stop bubbling
506
event.preventDefault() // Cancel default action
507
}
508
509
// Add event listeners
510
element.addEventListener('tap', handleTap)
511
element.addEventListener('longpress', (event) => {
512
console.log('Long press detected')
513
})
514
515
// Create custom events
516
const customEvent = createEvent({
517
type: 'custom',
518
detail: { customData: 'value' }
519
}, element)
520
521
// Dispatch events
522
element.dispatchEvent(customEvent)
523
```
524
525
### Event Handler
526
527
```typescript { .api }
528
import { eventHandler } from '@tarojs/runtime'
529
530
function eventHandler(handler: (event: TaroEvent) => void): (mpEvent: any) => void
531
```
532
533
The `eventHandler` function wraps standard event handlers to work with mini-program events:
534
535
```typescript
536
// Standard handler (receives TaroEvent)
537
function onTap(event: TaroEvent) {
538
console.log('Tapped:', event.target.tagName)
539
}
540
541
// Wrap for mini-program use
542
const mpHandler = eventHandler(onTap)
543
544
// Use in mini-program component
545
{
546
data: {},
547
onTap: mpHandler // Automatically converts mpEvent to TaroEvent
548
}
549
```
550
551
### EventSource
552
553
```typescript { .api }
554
import { eventSource } from '@tarojs/runtime'
555
556
interface EventSource {
557
// Global event registry for DOM nodes
558
set(node: TaroNode, event: TaroEvent): void
559
get(node: TaroNode): TaroEvent | null
560
delete(node: TaroNode): void
561
}
562
```
563
564
## Form Elements
565
566
### FormElement
567
568
```typescript { .api }
569
import { FormElement } from '@tarojs/runtime'
570
571
class FormElement extends TaroElement {
572
// Form-specific properties
573
type: string // Input type
574
value: string // Current value
575
576
// Automatic value synchronization on input/change events
577
}
578
```
579
580
#### Usage Examples
581
582
```typescript
583
// Create form elements
584
const input = document.createElement('input')
585
input.type = 'text'
586
input.value = 'Initial value'
587
588
const textarea = document.createElement('textarea')
589
textarea.value = 'Multi-line text'
590
591
// Value updates automatically sync with mini-program
592
input.addEventListener('input', (event) => {
593
console.log('New value:', input.value)
594
})
595
596
// Form submission
597
const form = document.createElement('form')
598
form.appendChild(input)
599
form.addEventListener('submit', (event) => {
600
event.preventDefault()
601
console.log('Form data:', input.value)
602
})
603
```
604
605
## SVG Support
606
607
### SVGElement
608
609
```typescript { .api }
610
import { SVGElement } from '@tarojs/runtime'
611
612
class SVGElement extends TaroElement {
613
// SVG-specific implementation
614
// Inherits all TaroElement functionality with SVG namespace handling
615
}
616
```
617
618
#### Usage Examples
619
620
```typescript
621
// Create SVG elements
622
const svg = document.createElement('svg')
623
svg.setAttribute('width', '100')
624
svg.setAttribute('height', '100')
625
626
const circle = document.createElement('circle')
627
circle.setAttribute('cx', '50')
628
circle.setAttribute('cy', '50')
629
circle.setAttribute('r', '25')
630
circle.setAttribute('fill', 'blue')
631
632
svg.appendChild(circle)
633
```
634
635
## Mutation Observer
636
637
### MutationObserver
638
639
```typescript { .api }
640
import { MutationObserver } from '@tarojs/runtime'
641
642
class MutationObserver {
643
constructor(callback: MutationCallback)
644
645
// Observation control
646
observe(target: Node, options?: MutationObserverInit): void
647
disconnect(): void
648
takeRecords(): MutationRecord[]
649
}
650
651
interface MutationRecord {
652
type: 'attributes' | 'characterData' | 'childList'
653
target: Node
654
addedNodes: NodeList
655
removedNodes: NodeList
656
previousSibling: Node | null
657
nextSibling: Node | null
658
attributeName: string | null
659
attributeNamespace: string | null
660
oldValue: string | null
661
}
662
663
type MutationCallback = (mutations: MutationRecord[], observer: MutationObserver) => void
664
```
665
666
#### Usage Examples
667
668
```typescript
669
// Create observer
670
const observer = new MutationObserver((mutations) => {
671
mutations.forEach((mutation) => {
672
console.log('Mutation type:', mutation.type)
673
console.log('Target:', mutation.target)
674
675
if (mutation.type === 'childList') {
676
console.log('Added nodes:', mutation.addedNodes.length)
677
console.log('Removed nodes:', mutation.removedNodes.length)
678
}
679
680
if (mutation.type === 'attributes') {
681
console.log('Attribute changed:', mutation.attributeName)
682
}
683
})
684
})
685
686
// Start observing
687
observer.observe(document.body, {
688
childList: true, // Watch for child additions/removals
689
attributes: true, // Watch for attribute changes
690
characterData: true, // Watch for text content changes
691
subtree: true // Watch entire subtree
692
})
693
694
// Stop observing
695
observer.disconnect()
696
697
// Get pending mutations
698
const mutations = observer.takeRecords()
699
```
700
701
## Platform-Specific Optimizations
702
703
### Component Aliasing
704
705
Elements are automatically mapped to platform-specific components:
706
707
```typescript
708
// Generic element
709
const view = document.createElement('view')
710
711
// Platform-specific mapping:
712
// Web: <div>
713
// WeChat: <view>
714
// Alipay: <view>
715
// etc.
716
```
717
718
### View Optimization
719
720
Special view types for performance optimization:
721
722
```typescript
723
// Performance-optimized views
724
const pureView = document.createElement('pure-view') // Static content
725
const staticView = document.createElement('static-view') // No event handling
726
const catchView = document.createElement('catch-view') // Catch touch events
727
const clickView = document.createElement('click-view') // Click-only events
728
```
729
730
### Update Optimization
731
732
The DOM uses path-based updates for efficient mini-program rendering:
733
734
```typescript
735
// Automatic path-based updates
736
const element = document.createElement('view')
737
element.textContent = 'Hello' // Updates: { "root.cn.[0].v": "Hello" }
738
739
element.style.color = 'red' // Updates: { "root.cn.[0].st.color": "red" }
740
```
741
742
The DOM implementation provides complete web compatibility while optimizing for mini-program performance, enabling seamless cross-platform development.