1. はじめに
このセクションは非規範です。
カスタムハイライトAPIは、ハイライト疑似要素(CSS Pseudo-Elements 4 § 3 ハイライト疑似要素参照)の概念を拡張し、 ウェブ開発者が任意のRangeオブジェクトのテキストをスタイルできるようにします。 これは、ユーザーエージェントによって定義される::selection、::inactive-selection、 ::spelling-error、 および'::grammar-error'に限定されずに利用できます。 これは、独自の選択範囲を実装する編集フレームワークや、 仮想化された文書上のページ内検索、 オンライン共同編集を表現する複数選択、 またはスペルチェックフレームワークなど、様々なシナリオで有用です。
カスタムハイライトAPIは、DOM構造に影響を与えず、 Rangeオブジェクトに基づいてテキストにスタイルを適用するプログラム的なハイライト追加・削除方法を提供します。 これはrangeオブジェクトを利用し、 ::highlight()疑似要素を介してアクセスできます。
One twoに黄色の背景色と青色の前景色を適用しています。 これは、
Highlightを
HighlightRegistry
に追加することで実現します(どちらも本仕様で新しく導入された概念です)。
Highlightは
Range
を含み、その境界点はOne twoのテキストを囲みます。
< style > : root :: highlight ( example-highlight ) { background-color : yellow ; color : blue ; } </ style > < body >< span > One</ span >< span > two</ span >< span > three…</ span > < script > let r= new Range(); r. setStart( document. body, 0 ); r. setEnd( document. body, 2 ); CSS. highlights. set( "example-highlight" , new Highlight( r)); </ script >
結果イメージ:
2. モジュールの相互作用
このモジュールはInfra標準[INFRA]とWebIDL[WebIDL]に依存します。
CSSおよびDOM標準[DOM]に関する一般的な知識を前提とし、 特にCSS Pseudo-Elements Module Level 4[css-pseudo-4]で定義された仕組みを ハイライト疑似要素の扱いに拡張します。 Selectors Level 4[selectors-4]仕様は、 疑似要素の一般動作を定義しています。
依存関係の全リストは参考文献を参照してください。
注: このドラフトは初期バージョンです。 今後成熟するにつれ、CSS-WGは独立したモジュールとして維持するか、 [css-pseudo-4]またはその後継バージョンに統合することを選択する可能性があります。
3. カスタムハイライトの設定
3.1. カスタムハイライトの作成
カスタムハイライトは、ドキュメントの一部を表すRangeの集合です。 必ずしも要素ツリーに収まる必要はなく、 ネスト構造を無視して任意に要素境界をまたぐことができます。 これらのドキュメント部分の外観に影響を与えたり (§ 4 カスタムハイライトのスタイリング参照)、 関連するイベントを処理したりするために利用できます (§ 6 イベント処理参照)。
カスタムハイライトは、Highlightオブジェクト、つまりsetlikeオブジェクトであり、そのセットエントリはAbstractRangeオブジェクトです。Rangeは、コンストラクタに渡すことでカスタムハイライトに追加することができ、
またはsetlikeオブジェクトの通常のAPIを使って
そのセットエントリを操作することもできます。
注: カスタムハイライト内のrangeはAbstractRange
オブジェクトなので、作者はRange
オブジェクトとStaticRange
オブジェクトのどちらを使うか選択できます。
詳細とその影響については§ 5.2 範囲の更新と無効化を参照してください。
enum {HighlightType ,"highlight" ,"spelling-error" }; ["grammar-error" Exposed =Window ]interface Highlight {constructor (AbstractRange ...);initialRanges setlike <AbstractRange >;attribute long ;priority attribute HighlightType ; };type
priority
属性の詳細は§ 4.2.5 重複ハイライトの優先順位を参照してください。
type
属性の詳細は§ 4.2.6 ハイライトタイプを参照してください。
Highlight(AbstractRange... initialRanges)
コンストラクタが呼び出されたとき、次の手順を実行します:
- highlightを新しい
Highlightオブジェクトとする。 - highlightの
priorityを0に設定する。 - highlightの
typeをhighlightに設定する。 initialRangesの各rangeについて、 rangeArgをIDL値をECMAScript値に変換した結果とし、 組み込みsetlikeのadd関数の手順を highlightをthis値とし、rangeArgを引数として実行する。- highlightを返す。
3.2. カスタムハイライトの登録
効果を持たせるためには、カスタムハイライトを登録し、ハイライトレジストリに入れる必要があります。
ハイライトレジストリは、highlights
属性を介してCSS
名前空間からアクセスでき、
登録済みカスタムハイライト全てを表します。
これは現在のグローバルオブジェクトの関連付けられたDocumentに対して有効です。
maplikeであり、通常のメソッドで更新できます。
map entriesは初期状態では空です。
カスタムハイライトが登録済みであると言うのは、それがハイライトレジストリに存在する場合です。 後で削除された場合、登録済みではなくなります。
partial namespace CSS {readonly attribute HighlightRegistry ; }; [highlights Exposed =Window ]interface {HighlightRegistry maplike <DOMString ,Highlight >; };
setメソッドを呼び出します。
これにより、組み込みmaplikeのset関数の手順が
this値としてコンテキストオブジェクトを、
keyArgとして渡されたカスタムハイライト名、
valueArgとして渡されたハイライトを使って実行されます。
カスタムハイライトを登録する際に割り当てるカスタムハイライト名は、スタイリング時(§ 4 カスタムハイライトのスタイリング参照)にハイライトを識別するために使われます。
注: カスタムハイライトを登録する際、 作者はカスタムハイライト名に有効なCSS識別子を使うことを推奨します。 有効な識別子でない名前を使うと、CSSでハイライトをスタイルするのが困難、場合によっては不可能になります。
注: 1つのカスタムハイライトに複数のカスタムハイライト名を登録することも可能です。 しかし、1つのハイライトに複数の名前でスタイルを当てると、 それぞれ独立したスタイルセットが割り当てられ、ペインティング時にそれらの競合するスタイルの重ね順を制御できません。 これは作者にとって制限となり、混乱するペインティング結果になる可能性があります(詳細は下記例参照)。 よって、スタイリング時はハイライトにつき1つの名前のみ使用することを推奨します。
< style > div :: highlight ( bar ) { color : red ; } div :: highlight ( foo ) { color : green ; } </ style > < body >< div > abc</ div > < script > let div= document. body. firstChild; let r= new Range(); r. setStart( div, 0 ); r. setEnd( div, 1 ); let h= new Highlight( r); CSS. highlights. set( 'foo' , h); CSS. highlights. set( 'bar' , h); </ script >
上記の例では、同じカスタムハイライトオブジェクトがfooとbarという名前で登録されています。
それぞれのスタイルルールは同じハイライトを対象とし、詳細度も同じなので、
作者はカスケード順で最後のルールが優先され、ハイライト部分が緑色になると予想するかもしれません。
しかし、各ハイライト名は独立したスタイルセットを持ち、ハイライトは名前ごとに描画されます。
この場合、fooがbarより先に登録されたため、
まずfooの色(緑)で描画され、次にbarの色(赤)で描画されます。
その結果、ハイライト部分は赤色に見えます。
4. カスタムハイライトのスタイリング
4.1. カスタムハイライト疑似要素:::highlight()
::highlight(<custom-highlight-name>)疑似要素(カスタムハイライト疑似要素とも呼ばれる)は、 文書内で含まれるまたは部分的に含まれる rangeのすべての登録済みカスタムハイライト(カスタムハイライト名 <custom-highlight-name>)があれば、それを表します。 <custom-highlight-name>は有効なCSS <ident-token>でなければなりません。
4.2. 処理モデル
4.2.1. 適用可能なプロパティ
カスタムハイライト疑似要素は、 組み込みのハイライト疑似要素と同様に、 限定されたプロパティのみでスタイリングできます。 詳細なリストはCSS Pseudo-Elements 4 § 3.2 ハイライトのスタイリングを参照してください。
4.2.2. デフォルトスタイル
UAはカスタムハイライト疑似要素に対してUAデフォルトスタイルシートでスタイルを定義してはなりません。 カスタムハイライト疑似要素は、その発生元要素のスタイルを継承します。
4.2.3. カスケードと継承
カスケードおよび継承は、 カスタムハイライト疑似要素においても 組み込みのハイライト疑似要素と同様に扱われます。 詳細はCSS Pseudo-Elements 4 § 3.5 カスケードと要素ごとのハイライトスタイルで定義されています。
4.2.4. ペインティング
カスタムハイライトの描画も 組み込みのハイライト疑似要素と同様に処理されます。 詳細はCSS Pseudo-Elements 4 § 3.4 ハイライト領域およびCSS Pseudo-Elements 4 § 3.6 ハイライトのペインティングで指定されています。 なお、以下の補足があります:
- collapsedなrangeは描画されません。
-
1つのカスタムハイライト内で重複するrangeは、重なった部分が単一の統合されたrangeとして描画されたかのように扱われます。
以下の例では、半透明の青背景で単一のハイライトが描画され、重なり部分が透けて二重に見えることはありません。
< style > :: highlight ( sample ) { background-color : rgba( 0 , 0 , 255 , 0.3 ); } </ style > < body > Lorem Ipsum.< script > let textNode= document. body. firstChild; let r1= new Range(); r1. setStart( textNode, 1 ); r1. setEnd( textNode, 5 ); let r2= new Range(); r2. setStart( textNode, 3 ); r2. setEnd( textNode, 7 ); CSS. highlights. set( "sample" , new Highlight( r1, r2)); </ script > つまり、正しいレンダリングは次のようになります:
Lorem Ipsum.しかし、次のような重複描画は誤りです:
Lorem Ipsum. - ハイライトオーバーレイの重なり順は、カスタムハイライトが組み込みのハイライト疑似要素よりも下になるように、 CSS Pseudo-Elements 4 § 3.6 ハイライトのペインティングで定義されたスタッキング順で描画されます。
- 複数のカスタムハイライトのハイライトオーバーレイ同士の相対的な重なり順は、 それらのpriority値で決まります(§ 4.2.5 重複ハイライトの優先順位参照)。
4.2.5. 重複ハイライトの優先順位
カスタムハイライトの
priority
属性は、その優先順位を定義します。
これはペインティング時のハイライトオーバーレイの重なり順を決めるために使われます(§ 4.2.4
ペインティング参照)。
より高いpriority値ほど重なり順が上になります。
priority
属性が明示的に設定されていない場合、デフォルトは数値0です。
2つ以上のカスタムハイライトが同じ数値priorityの場合、 より最近登録された方が有効priorityが高くなります。
< style > : root :: highlight ( foo ) { color : blue ; background-color : yellow ; } : root :: highlight ( bar ) { background-color : orange ; } </ style > < body > Some text< script > let textNode= document. body. firstChild; let r1= new Range(); r1. setStart( textNode, 0 ); r1. setEnd( textNode, 6 ); let r2= new Range(); r2. setStart( textNode, 3 ); r2. setEnd( textNode, 9 ); let h1= new Highlight( r1); let h2= new Highlight( r2); CSS. highlights. set( "foo" , h1); CSS. highlights. set( "bar" , h2); </ script >
priorityが設定されていない(h1とh2が同順位)の場合、 カスタムハイライトのスタイルはハイライトレジストリへの挿入順で重ねられます。 描画結果は「Som」が青文字・黄背景、「e t」が青文字・オレンジ背景、「ext」がデフォルト色・オレンジ背景になります。
h1
を設定すると、h1がh2より上位になり、「Some t」が青文字・黄背景、「ext」がデフォルト色・オレンジ背景になります。
4.2.6. ハイライトタイプ
カスタムハイライトの
type
属性は、作者がハイライトの意味的役割を指定するために使います。
これにより、支援技術がユーザーに意味を伝えることができます。
type属性を明示的に設定しなかった場合、デフォルトはhighlightです。
注:作者はミススペル強調には
カスタムハイライトのtype
をspelling-errorに、
文法誤り強調にはgrammar-errorに設定することを推奨します。
その他用途ではtypeはhighlightのままで問題ありません。
UAはカスタムハイライトを支援技術へ提供すべきです。 ハイライトをプラットフォームのアクセシビリティAPIで公開する際、 UAはtype属性で指定された意味を可能な限り具体的に伝える必要があります。
注:例えば、プラットフォームのアクセシビリティAPIがスペルミスや文法誤りを明示できる場合は、 UAはtype=spelling-errorやtype=grammar-errorの意味をそのまま使います。 スペルミスのみ表現可能なAPIなら、spelling-errorとgrammar-error両方をスペルミスとして扱います。 どちらも表現できないAPIなら、すべてのハイライトはtype属性に関係なくhighlightとして公開されます。
注:この初期タイプセットは、Highlight
APIでよく使われるであろうケースと、現行のアクセシビリティAPIに一部サポートがあることから選定されました。
他のHighlight API用途には現状アクセシビリティAPIで表現手段がありません。
今後、新たな用途が普及しアクセシビリティAPIで表現可能になれば、HighlightTypeにタイプ追加される可能性があります。
5. 変更への対応
5.1. 再描画
ハイライトレジストリ内のカスタムハイライトの追加や削除、 または登録済みカスタムハイライト内のrangeの追加や削除は、 ユーザーエージェントがレンダリングを再評価し、必要に応じて再描画する原因となります。
また、作者によるpriority
の変更や、境界点によるRangeの変更に応じても
ユーザーエージェントはハイライトを再描画しなければなりません。
この再評価のタイミング(同期性含む)をどう規定すべきか? [Issue #4596]
5.2. 範囲の更新と無効化
作者はカスタムハイライトをRange
またはStaticRangeで構築できます。
結果として得られるカスタムハイライトは 同じ文書部分を表し、同じようにスタイリングできます。 ただし、基礎となる文書が変更された場合の挙動は異なります。
Rangeはライブ範囲です。
ユーザーエージェントは、範囲またはその境界に重なるDOM変更に応じてRangeの境界点を調整し、
それに応じて再描画します。ライブ範囲の境界点は作者によって変更することもできます。
一方、ユーザーエージェントはStaticRangeの境界点を
DOM変更に応じて調整してはならず、作成後に作者が変更することもできません。
ユーザーエージェントは実際のStaticRangeを保持し、
ライブRangeによるバックアップはしません。
Range
オブジェクトを更新することは大きなパフォーマンスコストになります。
DOM変更を監視して反応し、カスタムハイライト内の範囲を調整・再作成したい作者は、
不要なコストを避けるためStaticRangeの利用を強く推奨します。
逆に、StaticRangeを使う作者は、
DOM変更を監視して反応し、
古くなったrangeやカスタムハイライトを破棄し新たに作成すべきです。
文書のレンダリング計算時、
その文書ウィンドウに関連付けられたハイライトレジストリ内の
開始ノードや終了ノード
が、その文書でないNodeのshadow-including rootを参照する場合、
ユーザーエージェントはそのrangeを無視しなければなりません。
また、その文書ウィンドウに関連付けられたハイライトレジストリ内のStaticRangeが有効でない場合も、
ユーザーエージェントはそのrangeを無視しなければなりません。
StaticRangeを含むカスタムハイライトと[css-contain-2]との相互作用は問題があるように思われる:
完全に包含された要素であれば、その子孫へのDOM変更は外部要素の無効化や再描画を引き起こさないはずだが、
static rangeの境界点が包含サブツリー内と外部両方にあり、
包含サブツリー内のDOMが変更されて境界点が有効ノードでなくなった場合、
範囲全体が無視され、外部サブツリーの描画にも影響する。
これはスタイル包含の弱点か、
それとも上記無効化ロジックの弱点か、あるいは他の問題か? [Issue #4598]
6. イベント処理
イベントに関するセクションは未定です。https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/master/highlight/events-explainer.md を参照してください。
カスタムハイライト専用のイベント処理機構を持つべきか、疑似要素全般に追加すべきか検討中です。
付録A. プライバシーとセキュリティに関する考慮事項
このセクションは非規範です。
本仕様が新たなセキュリティやプライバシー上の懸念を引き起こすとは考えられていません。 もしそうでない疑いがある場合は、CSSワーキンググループまたは共同編集者までご連絡ください。
付録C. 変更履歴
このセクションは非規範です。
2020年12月8日作業草案以降の変更点 8 December 2020 Working Draft
様々な編集改善や細かな調整に加え、主な変更点は以下の通りです:
-
HighlightsRegisterをHighlightRegistryに名称変更 -
HighlightRegistryから冗長なadd()メソッドを削除 (Issue 6092参照) -
カスタムハイライトオーバーレイがネイティブハイライトオーバーレイより下に重なるように変更 (Issue 4595参照)
-
ハイライトの優先順位をfloat型から整数型で扱うように変更 (Issue 4592参照)
-
ハイライトの優先順位のデフォルト値を0に定義 (Issue 6136参照)
-
HighlightRegistryをsetlikeからmaplikeに変更し、Highlightから
nameプロパティを削除 (Issue 5910参照) -
異なるwindowのrangeは描画されないことを明確化 (Issue 6417参照)
-
カスタムハイライトはUAスタイルを持たないことを明記 (Issue 6375参照)
-
rangeの無効化は[DOM]仕様に委譲 (Issue 4597参照)
-
type属性をHighlightに追加し、 ハイライトの意味を明確化、アクセシビリティツールへの公開をサポート (Issue 6498参照)
2020年10月22日作業草案以降の変更点 22 October 2020 Working Draft
2020年10月22日作業草案以降は編集上の変更のみです。 詳細は差分を参照してください。