Skip to content

Latest commit

 

History

History
139 lines (102 loc) · 5.19 KB

DOM.md

File metadata and controls

139 lines (102 loc) · 5.19 KB

@holoflows/kit/DOM/

提供了一组方便追踪被注入页面中内容变化的工具。

我们先来看一个例子:

假设被注入页面是由 React 生成的机票价格页面,它会动态刷新。你想在每张机票的价格后面加上它的美元价格。

import { LiveSelector, MutationObserverWatcher } from '@holoflows/kit/DOM'

const price = new LiveSelector()
    // 选择所有的 .ticket-row
    .querySelector<HTMLDivElement>('.ticket-row')
    // 排除那些没有欧元符号的行
    .filter(x => x.innerText.match('€'))
    // 把每个元素映射成它里面的 .price
    .map(x => x.querySelector('.price'))

function EuroToUSD(x: number) {
    return x * 1.13
}

new MutationObserverWatcher(price, document.querySelector('#main'))
    .useForeach((node, key, meta) => {
        const addPrice = () => (meta.after.innerText = '$' + EuroToUSD(parseInt(node.innerText)))
        addPrice()
        return {
            onNodeMutation: addPrice,
        }
    })
    .startWatch()

大功告成,每当有新机票出现,就会自动在后面加上一个美元价格!不过慢点,我还没搞懂!

上面是一个常规的用法,展示了 @holoflows/kit 的简洁与强大之处。

看完了下面的文档再来回头看看上面这段例子,你就能明白了!

一切都要从 LiveSelector 开始…… LiveSelector 是一个方便的工具,可以帮你多次计算同一个 "Selector" 的值,而且它很像数组,可以直接执行一些方便的操作。
const ls = new LiveSelector()
ls.querySelectorAll('a') // 选择所有的 a
ls.filter(x => x.href.startsWith('https://')) // 去掉所有不以 https:// 开头的链接
ls.map(x => x.href) // 把 HTMLAnchorElement[] 映射成 string[]

ls.evaluate() // 返回当前页面上所有的链接列表
setTimeout(() => {
    ls.evaluate() // 可以多次调用!每次都会返回页面上最新的符合 LiveSelector 的结果!
})

关于 LiveSelector 的完整用法,参见 LiveSelector 的文档

那么 MutationObserverWatcher 又是什么?

Watcher 可以根据某些特定的条件自动执行 `LiveSelector` 的 `evaluate()`,然后通过比较两次列表的变化通知你 LiveSelector 发生了哪些更改。 简而言之,就是可以监听指定内容的变化。 Watcher 有以下几种:

所有 Watcher 的使用方法都是一样的:

  • 要让 Watcher 开始监听网页变化,你都需要调用 startWatch()
  • 要停止 Watcher,需要 stopWatch()

一般情况下,Watcher 关注的都是 DOM 的变化,如果你希望关注其他内容的变化,Watcher 提供了 onAdd onRemove 等的事件,具体请参阅Watcher 的文档

这是我们来关注 DOM 变化的主要办法。如果你了解 React hooks 的话,这个和那个很像。

简单的说,一个完整的 useForeach 调用是这样的

.useForeach((node, key, meta) => {
    // 这里的代码会在 **每次** 有一个新的 元素 E 进入列表的时候调用。以下是传入的参数:
    node // 是一种叫 DOMProxy 的对象
    meta.before // 是一个 <span> 始终指向 E 的前面
    meta.after // 是一个 <span> 始终指向 E 的后面
    meta.current // 它就是 node(第一个参数),它始终指向 E,就算 E 换了,它的引用也会自动"更新"(事实并非如此,请参见 DOMProxy 的文档)

    key // 用过 React, Vue 或者 Angular 吗?在渲染列表的时候它们都会要求你提供 key 以保证复用。这就和那个差不多。

    meta.realCurrent // 有时候你就是想访问真实的 DOM 元素,那就用它吧。

    return {
        onRemove(old) {
            // 如果 E 从文档里消失了……
            // 你需要做的善后工作……
            // 类似于 React.useEffect(() => { return 这里返回的函数 })
        },
        onNodeMutation() {
            // 如果 key 没变,E 也没变
            // 只是 E 内部发生了变化的话,这里会被通知到
            // 比如 node.current 里面新插入了一个元素
        },
        onTargetChanged(newNode, oldNode) {
            // 如果 key 没变,但是 key 指向的 E 变了的话
            // oldNode 是变化前指向的元素,newNode 是变化后指向的新元素
            // 注意,node.current 始终指向 newNode,所以很多事情你不必手动处理
        },
    }
})

Tips: 如果你始终只关心 LiveSelector 选中的第一个元素的话,这里有个捷径!Watcher#firstDOMProxy 是一个始终指向第一个元素的 DOMProxy

Watcher 的文档

教程到这里就结束了,你可以回头看看最开始的例子。