跳转到内容
搜索文档

HTMLRewriter

最后更新 查看 MarkdownAgent 设置

背景

HTMLRewriter 类允许开发者在 Cloudflare Workers 应用程序中构建全面且富有表现力的 HTML 解析器。可以将其视为直接在 Workers 应用程序中使用的类似 jQuery 的体验。借助强大的 JavaScript API 来解析和转换 HTML,HTMLRewriter 使开发者能够构建功能丰富的应用程序。

HTMLRewriter 类应在 Workers 脚本中实例化一次,并使用 ononDocument 函数附加多个 handler。


构造函数

new HTMLRewriter()
	.on("*", new ElementHandler())
	.onDocument(new DocumentHandler());

全局类型

HTMLRewriter API 中,许多属性和方法使用几种一致的类型:

  • Content string | Response | ReadableStream

  • ContentOptions Object

    • { html: Boolean } 控制 HTMLRewriter 处理插入内容的方式。如果 html 布尔值设为 true,内容将被视为原始 HTML。如果 html 布尔值设为 false 或未提供,内容将被视为文本并应用适当的 HTML 转义。

Handler

HTMLRewriter 可使用两种 handler 类型:元素 handler 和文档 handler。

元素 Handler

元素 handler 响应任何传入元素,通过 HTMLRewriter 实例的 .on 函数附加。元素 handler 应响应 elementcommentstext。以下示例使用 ElementHandler 类处理 div 元素。

class ElementHandler {
	element(element) {
		// An incoming element, such as `div`
		console.log(`Incoming element: ${element.tagName}`);
	}

	comments(comment) {
		// An incoming comment
	}

	text(text) {
		// An incoming piece of text
	}
}

async function handleRequest(req) {
	const res = await fetch(req);

	return new HTMLRewriter().on("div", new ElementHandler()).transform(res);
}

文档 Handler

文档 handler 表示传入的 HTML 文档。可以在文档 handler 上定义多个函数来查询和操作文档的 doctypecommentstextend。与元素 handler 不同,文档 handler 的 doctypecommentstextend 函数不受特定选择器范围限制。文档 handler 的函数会为页面上的所有内容调用,包括顶层 HTML 标签之外的内容:

class DocumentHandler {
	doctype(doctype) {
		// An incoming doctype, such as <!DOCTYPE html>
	}

	comments(comment) {
		// An incoming comment
	}

	text(text) {
		// An incoming piece of text
	}

	end(end) {
		// The end of the document
	}
}

异步 Handler

元素 handler 和文档 handler 上定义的所有函数都可以返回 voidPromise<void>。将 handler 函数设为 async 允许你访问外部资源,例如通过 fetch、Workers KV、Durable Objects 或缓存访问 API。

class UserElementHandler {
	async element(element) {
		let response = await fetch(new Request("/user"));

		// fill in user info using response
	}
}

async function handleRequest(req) {
	const res = await fetch(req);

	// run the user element handler via HTMLRewriter on a div with ID `user_info`
	return new HTMLRewriter()
		.on("div#user_info", new UserElementHandler())
		.transform(res);
}

Element

element 参数仅在元素 handler 中使用,是 DOM 元素的表示。元素上有多个方法可用于查询和操作:

属性

  • tagName string

    • 标签名称,例如 "h1""div"。此属性可以赋不同值以修改元素的标签。
  • attributes Iterator read-only

    • 标签属性的 [name, value] 对。
  • removed boolean

    • 指示元素是否已被之前的 handler 删除或替换。
  • namespaceURI string

方法

  • getAttribute(name string) : string | null

    • 返回元素上给定属性名称的值,如果未找到则返回 null
  • hasAttribute(name string) : boolean

    • 返回布尔值,指示元素上是否存在某个属性。
  • setAttribute(name string, value string) : Element

    • 将属性设置为提供的值,如果属性不存在则创建它。
  • removeAttribute(name string) : Element

    • 删除属性。
  • before(content Content, contentOptions ContentOptionsoptional) : Element

    • 在元素之前插入内容。
  • after(content Content, contentOptions ContentOptionsoptional) : Element

    • 在元素之后立即插入内容。
  • prepend(content Content, contentOptions ContentOptionsoptional) : Element

    • 在元素开始标签之后立即插入内容。
  • append(content Content, contentOptions ContentOptionsoptional) : Element

    • 在元素结束标签之前立即插入内容。
  • replace(content Content, contentOptions ContentOptionsoptional) : Element

    • 删除元素并在其位置插入内容。
  • setInnerContent(content Content, contentOptions ContentOptionsoptional) : Element

    • 替换元素的内容。
  • remove() : Element

    • 删除元素及其所有内容。
  • removeAndKeepContent() : Element

    • 删除元素的开始标签和结束标签,但保留其内部内容。
  • onEndTag(handler Function<void>) : void

    • 注册在到达元素结束标签时调用的 handler。

EndTag

endTag 参数仅在通过 element.onEndTag 注册的 handler 中使用,是 DOM 元素的有限表示。

属性

  • name string
    • 标签名称,例如 "h1""div"。此属性可以赋不同值以修改元素的标签。

方法

  • before(content Content, contentOptions ContentOptionsoptional) : EndTag

    • 在结束标签之前插入内容。
  • after(content Content, contentOptions ContentOptionsoptional) : EndTag

    • 在结束标签之后立即插入内容。
  • remove() : EndTag

    • 删除元素及其所有内容。

文本块

由于 Cloudflare 执行零拷贝流式解析,文本块与词法树中的文本节点不是同一概念。词法树文本节点可以由多个块表示,这些块从源站通过网络逐个到达。

考虑以下标记:<div>Hey. How are you?</div>。Workers 脚本可能不会一次性从源站收到整个文本节点;相反,text 元素 handler 会为文本节点的每个接收部分调用。例如,handler 可能先被调用 "Hey. How ",然后是 "are you?"。当最后一个块到达时,text 的 lastInTextNode 属性将设为 true。开发者应确保将这些块连接在一起。

属性

  • removed boolean

    • 指示元素是否已被之前的 handler 删除或替换。
  • text string read-only

    • 块的文本内容。如果块是文本节点的最后一个块,可能为空。
  • lastInTextNode boolean read-only

    • 指定该块是否为文本节点的最后一个块。

方法

  • before(content Content, contentOptions ContentOptionsoptional) : Element

    • 在元素之前插入内容。
  • after(content Content, contentOptions ContentOptionsoptional) : Element

    • 在元素之后立即插入内容。
  • replace(content Content, contentOptions ContentOptionsoptional) : Element

    • 删除元素并在其位置插入内容。
  • remove() : Element

    • 删除元素及其所有内容。

注释

元素 handler 上的 comments 函数允许开发者查询和操作 HTML 注释标签。

class ElementHandler {
	comments(comment) {
		// An incoming comment element, such as <!-- My comment -->
	}
}

属性

  • comment.removed boolean

    • 指示元素是否已被之前的 handler 删除或替换。
  • comment.text string

    • 注释的文本。此属性可以赋不同值以修改注释文本。

方法

  • before(content Content, contentOptions ContentOptionsoptional) : Element

    • 在元素之前插入内容。
  • after(content Content, contentOptions ContentOptionsoptional) : Element

    • 在元素之后立即插入内容。
  • replace(content Content, contentOptions ContentOptionsoptional) : Element

    • 删除元素并在其位置插入内容。
  • remove() : Element

    • 删除元素及其所有内容。

Doctype

文档 handler 上的 doctype 函数允许开发者查询文档的 doctype

class DocumentHandler {
	doctype(doctype) {
		// An incoming doctype element, such as
		// <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
	}
}

属性

  • doctype.name string | null read-only

    • doctype 名称。
  • doctype.publicId string | null read-only

    • doctype 中 PUBLIC 原子之后的引号字符串。
  • doctype.systemId string | null read-only

    • doctype 中 SYSTEM 原子之后或 publicId 之后立即出现的引号字符串。

End

文档 handler 上的 end 函数允许开发者在文档末尾追加内容。

class DocumentHandler {
	end(end) {
		// The end of the document
	}
}

方法

  • append(content Content, contentOptions ContentOptionsoptional) : DocumentEnd

    • 在文档结束之后插入内容。

选择器

以下是选择器及其用途。

  • *

    • 任何元素。
  • E

    • 任何 E 类型的元素。
  • E:nth-child(n)

    • E 元素,其父元素的第 n 个子元素。
  • E:first-child

    • E 元素,其父元素的第一个子元素。
  • E:nth-of-type(n)

    • E 元素,其同类型兄弟中的第 n 个。
  • E:first-of-type

    • E 元素,其同类型的第一个兄弟。
  • E:not(s)

    • 不匹配任一复合选择器的 E 元素。
  • E.warning

    • 属于 warning 类的 E 元素。
  • E#myid

    • ID 等于 myid 的 E 元素。
  • E[foo]

    • 具有 foo 属性的 E 元素。
  • E[foo="bar"]

    • foo 属性值完全等于 bar 的 E 元素。
  • E[foo="bar" i]

    • foo 属性值完全等于 bar 的任意(ASCII 范围)大小写排列的 E 元素。
  • E[foo="bar" s]

    • foo 属性值完全且大小写敏感地等于 bar 的 E 元素。
  • E[foo~="bar"]

    • foo 属性值为空格分隔的值列表且其中一项完全等于 bar 的 E 元素。
  • E[foo^="bar"]

    • foo 属性值以 bar 字符串开头的 E 元素。
  • E[foo$="bar"]

    • foo 属性值以 bar 字符串结尾的 E 元素。
  • E[foo*="bar"]

    • foo 属性值包含 bar 子字符串的 E 元素。
  • E[foo|="en"]

    • foo 属性值为以 en 开头的连字符分隔值列表的 E 元素。
  • E F

    • E 元素的后代 F 元素。
  • E > F

    • E 元素的子元素 F。

错误

如果 handler 抛出异常,解析会立即停止,转换后的响应 body 会因抛出的异常而报错,未转换的响应 body 会被取消(关闭)。如果转换后的响应 body 已经部分流式传输回客户端,客户端将看到截断的响应。

async function handle(request) {
	let oldResponse = await fetch(request);
	let newResponse = new HTMLRewriter()
		.on("*", {
			element(element) {
				throw new Error("A really bad error.");
			},
		})
		.transform(oldResponse);

	// At this point, an expression like `await newResponse.text()`
	// will throw `new Error("A really bad error.")`.
	// Thereafter, any use of `newResponse.body` will throw the same error,
	// and `oldResponse.body` will be closed.

	// Alternatively, this will produce a truncated response to the client:
	return newResponse;
}

相关资源

这篇文档对您有帮助吗?