Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

HTMLRewriter

最終更新 Markdown で表示Agent セットアップ

背景

HTMLRewriter クラスは、Cloudflare Workers アプリ内で包括的で表現力のある HTML パーサーを構築できるようにします。Workers アプリ内で、jQuery のような体験だと考えると分かりやすいです。強力な JavaScript API で HTML を解析・変換し、深く機能するアプリを作れます。

HTMLRewriter クラスは、Workers スクリプト内で一度インスタンス化し、ononDocument 関数で複数のハンドラーを付けます。


コンストラクター

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

グローバル型

HTMLRewriter API 全体で、多くのプロパティとメソッドが次の型を一貫して使います。

  • Content string | Response | ReadableStream

    • 出力ストリームに挿入するコンテンツは、文字列、Response、または ReadableStream です。
  • ContentOptions Object

    • { html: Boolean } は、HTMLRewriter が挿入コンテンツを扱う方法を制御します。html が true の場合、コンテンツは生の HTML として扱われます。html が false または未指定の場合、コンテンツはテキストとして扱われ、適切な HTML エスケープが適用されます。

ハンドラー

HTMLRewriter で使えるハンドラーは、要素ハンドラーとドキュメントハンドラーの 2 種類です。

要素ハンドラー

要素ハンドラーは、HTMLRewriter インスタンスの .on 関数で付けると、着信する要素に応答します。要素ハンドラーは 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);
}

ドキュメントハンドラー

ドキュメントハンドラーは、着信する HTML ドキュメントを表します。ドキュメントハンドラーには、ドキュメントの doctypecommentstextend を照会・操作する関数を定義できます。要素ハンドラーと異なり、ドキュメントハンドラーの doctypecommentstextend は特定のセレクターでスコープされません。ドキュメントハンドラーの関数は、トップレベルの 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
	}
}

非同期ハンドラー

要素ハンドラーとドキュメントハンドラーのすべての関数は、void または Promise<void> を返せます。ハンドラー関数を async にすると、fetch 経由の API、Workers KV、Durable Objects、キャッシュなどの外部リソースにアクセスできます。

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 引数は、DOM 要素の表現です。要素を照会・操作するメソッドがいくつかあります。

プロパティ

  • tagName string

    • タグ名です。例: "h1""div"。別の値を代入して、要素のタグを変更できます。
  • attributes Iterator read-only

    • タグの属性の [name, value] ペアです。
  • removed boolean

    • 要素が、前のハンドラーのいずれかによって削除または置換されたかを示します。
  • namespaceURI string

メソッド

  • getAttribute(name string) : string | null

    • 要素上の指定した属性名の値を返します。見つからない場合は null です。
  • hasAttribute(name string) : boolean

    • 要素に属性が存在するかを示す boolean を返します。
  • setAttribute(name string, value string) : Element

    • 属性を指定した値に設定します。存在しない場合は作成します。
  • removeAttribute(name string) : Element

    • 属性を削除します。
  • before(content Content, contentOptions ContentOptions optional) : Element

    • 要素の前にコンテンツを挿入します。
  • after(content Content, contentOptions ContentOptions optional) : Element

    • 要素の直後にコンテンツを挿入します。
  • prepend(content Content, contentOptions ContentOptions optional) : Element

    • 要素の開始タグの直後にコンテンツを挿入します。
  • append(content Content, contentOptions ContentOptions optional) : Element

    • 要素の終了タグの直前にコンテンツを挿入します。
  • replace(content Content, contentOptions ContentOptions optional) : Element

    • 要素を削除し、その場所にコンテンツを挿入します。
  • setInnerContent(content Content, contentOptions ContentOptions optional) : Element

    • 要素のコンテンツを置き換えます。
  • remove() : Element

    • 要素とその中身をすべて削除します。
  • removeAndKeepContent() : Element

    • 要素の開始タグと終了タグを削除し、内側のコンテンツは残します。
  • onEndTag(handler Function<void>) : void

    • 要素の終了タグに到達したときに呼ばれるハンドラーを登録します。

EndTag

element.onEndTag で登録したハンドラーでのみ使う endTag 引数は、DOM 要素の限定的な表現です。

プロパティ

  • name string
    • タグ名です。例: "h1""div"。別の値を代入して、要素のタグを変更できます。

メソッド

  • before(content Content, contentOptions ContentOptions optional) : EndTag

    • 終了タグの直前にコンテンツを挿入します。
  • after(content Content, contentOptions ContentOptions optional) : EndTag

    • 終了タグの直後にコンテンツを挿入します。
  • remove() : EndTag

    • 要素とその中身をすべて削除します。

テキストチャンク

Cloudflare はゼロコピーのストリーミング解析を行うため、テキストチャンクは字句木のテキストノードと同じではありません。字句木の 1 つのテキストノードが、オリジンから回線経由で届く複数のチャンクとして表されることがあります。

次のマークアップを考えます: <div>Hey. How are you?</div>。Workers スクリプトがオリジンからテキストノード全体を一度に受け取らないことがあります。その場合、text 要素ハンドラーは、テキストノードの受け取った各部分に対して呼ばれます。たとえば、まず "Hey. How "、次に "are you?" で呼ばれることがあります。最後のチャンクが届くと、テキストの lastInTextNode プロパティが true になります。これらのチャンクは連結してください。

プロパティ

  • removed boolean

    • 要素が、前のハンドラーのいずれかによって削除または置換されたかを示します。
  • text string read-only

    • チャンクのテキスト内容です。テキストノードの最後のチャンクである場合、空になることがあります。
  • lastInTextNode boolean read-only

    • チャンクがテキストノードの最後のチャンクかどうかを示します。

メソッド

  • before(content Content, contentOptions ContentOptions optional) : Element

    • 要素の前にコンテンツを挿入します。
  • after(content Content, contentOptions ContentOptions optional) : Element

    • 要素の直後にコンテンツを挿入します。
  • replace(content Content, contentOptions ContentOptions optional) : Element

    • 要素を削除し、その場所にコンテンツを挿入します。
  • remove() : Element

    • 要素とその中身をすべて削除します。

コメント

要素ハンドラーの comments 関数で、HTML コメントタグを照会・操作できます。

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

プロパティ

  • comment.removed boolean

    • 要素が、前のハンドラーのいずれかによって削除または置換されたかを示します。
  • comment.text string

    • コメントのテキストです。別の値を代入して、コメントのテキストを変更できます。

メソッド

  • before(content Content, contentOptions ContentOptions optional) : Element

    • 要素の前にコンテンツを挿入します。
  • after(content Content, contentOptions ContentOptions optional) : Element

    • 要素の直後にコンテンツを挿入します。
  • replace(content Content, contentOptions ContentOptions optional) : Element

    • 要素を削除し、その場所にコンテンツを挿入します。
  • remove() : Element

    • 要素とその中身をすべて削除します。

Doctype

ドキュメントハンドラーの 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

ドキュメントハンドラーの end 関数で、ドキュメントの末尾にコンテンツを追加できます。

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

メソッド

  • append(content Content, contentOptions ContentOptions optional) : DocumentEnd

    • ドキュメントの末尾のあとにコンテンツを挿入します。

セレクター

セレクターの種類と用途は次のとおりです。

  • *

    • 任意の要素。
  • E

    • 型 E の任意の要素。
  • E:nth-child(n)

    • 親の n 番目の子である E 要素。
  • E:first-child

    • 親の最初の子である E 要素。
  • E:nth-of-type(n)

    • 同じ型の n 番目の兄弟である E 要素。
  • 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 属性の値が空白区切りのリストで、そのうち 1 つが 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 要素。

エラー

ハンドラーが例外を投げると、解析はただちに止まり、変換後の応答本文はその例外でエラーになり、未変換の応答本文はキャンセル(クローズ)されます。変換後の応答本文がすでにクライアントへ部分的にストリーミングされていた場合、クライアントには切り詰められた応答が見えます。

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;
}

関連リソース

役に立ちましたか?