| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667 |
- /** overlay 的最小 DOM adapter:只验证节点/样式契约,不模拟浏览器布局或动画。 */
- export function markerTestDocument() {
- class Element {
- constructor(tag) {
- this.tagName = tag
- this.nodeType = 1
- this.children = []
- this.dataset = {}
- this.attributes = {}
- this.className = ''
- this.listeners = new Map()
- this.style = { setProperty(key, value) { this[key] = value } }
- this.classList = { toggle: (name, enabled) => {
- const names = new Set(this.className.split(' ').filter(Boolean))
- if (enabled) names.add(name)
- else names.delete(name)
- this.className = [...names].join(' ')
- } }
- }
- get parentElement() { return this.parentNode }
- get isConnected() { return this.connected === true || Boolean(this.parentNode?.isConnected) }
- appendChild(child) {
- child.parentNode?.removeChild(child)
- child.parentNode = this
- this.children.push(child)
- return child
- }
- removeChild(child) {
- this.children = this.children.filter((item) => item !== child)
- child.parentNode = null
- return child
- }
- remove() { this.parentNode?.removeChild(this) }
- set innerHTML(value) {
- // overlay 的水滴 SVG 占位;不解析路径、不伪造像素证据。
- this.html = value
- this.firstChild = new Element('svg')
- this.firstChild.inner = new Element('image')
- }
- setAttribute(key, value) { this.attributes[key] = value }
- querySelector(selector) { return selector === '[data-marker-overlay-inner]' ? this.inner : null }
- addEventListener(type, fn) {
- const listeners = this.listeners.get(type) || []
- this.listeners.set(type, [...listeners, fn])
- }
- removeEventListener(type, fn) {
- this.listeners.set(type, (this.listeners.get(type) || []).filter((item) => item !== fn))
- }
- contains(node) { return node === this || this.children.some((child) => child.contains(node)) }
- closest(selector) {
- return this.className.split(' ').includes(selector.slice(1)) ? this : this.parentNode?.closest(selector)
- }
- getBoundingClientRect() { return { left: 0, top: 0, width: 1000, height: 800 } }
- nodes() { return [this, ...this.children.flatMap((child) => child.nodes())] }
- fire(type, target) {
- for (const fn of this.listeners.get(type) || []) fn({ target, clientX: 100, clientY: 200, stopPropagation() {} })
- }
- }
- const head = new Element('head')
- const host = new Element('main')
- head.connected = host.connected = true
- return {
- head, host,
- createElement: (tag) => new Element(tag),
- getElementById: (id) => head.nodes().find((node) => node.id === id) || null,
- }
- }
|