1. 소개
이 섹션은 비규범적입니다.
커스텀 하이라이트 API는 하이라이트 의사요소(CSS Pseudo-Elements 4 § 3 하이라이트 의사요소 참조) 개념을 확장하여, 웹 개발자가 임의의 Range 객체의 텍스트를 스타일링할 수 있게 합니다. 이는 사용자 에이전트가 정의한 ::selection, ::inactive-selection, ::spelling-error, 그리고 '::grammar-error' 만으로 제한되지 않습니다. 이 기능은 다양한 상황에서 유용합니다. 예를 들면 자체 선택 기능을 구현하려는 편집 프레임워크, 가상화된 문서에서의 페이지 내 찾기, 온라인 협업을 위한 다중 선택, 또는 맞춤법 검사 프레임워크 등이 있습니다.
커스텀 하이라이트 API는 기본 DOM 구조에 영향을 주지 않으면서 프로그래밍적으로 하이라이트를 추가 및 제거할 수 있는 방법을 제공합니다. 대신 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 객체로서 set entries는 AbstractRange
객체가 됩니다. Range들은 생성자에 전달하거나,
setlike 객체의 일반적인 API를 사용하여 set
entries를 조작함으로써 커스텀 하이라이트에 추가할 수 있습니다.
참고: 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
§ 4.2.5 겹치는 하이라이트의 우선순위에서 priority
속성에 대한 자세한 정보를 확인할 수 있습니다.
§ 4.2.6 하이라이트 종류에서 type
속성에 대한 자세한 정보를 확인할 수 있습니다.
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 함수의
절차를 실행합니다.
context object를 this
값으로,
전달된 커스텀 하이라이트
이름을 keyArg로,
전달된 하이라이트를 valueArg로 사용합니다.
커스텀 하이라이트 이름은 커스텀 하이라이트가 등록될 때 지정되며, 스타일링 시 하이라이트를 식별하는 데 사용됩니다 (§ 4 커스텀 하이라이트 스타일링 참고).
참고: 커스텀 하이라이트를 등록할 때, 작성자는 올바른 CSS 식별자에 해당하는 커스텀 하이라이트 이름을 사용할 것을 권장합니다. 올바르지 않은 식별자를 사용하면 CSS로 하이라이트를 스타일링하기 어렵거나, 경우에 따라 불가능할 수 있습니다.
참고: 하나의 커스텀 하이라이트를 여러 커스텀 하이라이트 이름으로 등록하는 것도 가능합니다. 그러나 여러 이름을 사용해 하이라이트를 스타일링하면, 각 이름마다 서로 다른 스타일 집합이 적용되고, 이 집합 내에서 상충하는 스타일의 스택 순서를 제어할 방법이 없습니다 (페인팅 시 이를 제어할 수 없습니다). 이는 작성자에게 제한적일 수 있고, 혼란스러운 페인팅 결과를 초래할 수 있습니다 (아래 예시 참고). 따라서 스타일링 시 하이라이트당 하나의 이름만 사용하는 것이 좋습니다.
< 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 하이라이트 페인팅에 명시되어 있습니다. 아래와 같은 추가 설명이 있습니다:
- 접힘 range는 렌더링되지 않습니다.
-
하나의 커스텀 하이라이트
내에서 겹치는 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 속성을 명시적으로 설정하지 않으면 기본값은 0입니다.
둘 이상의 커스텀 하이라이트가
동일한 우선순위 값을 가지는 경우,
가장 최근에 등록된 것이 더 높은
실제 우선순위를 가집니다.
< 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 >
우선순위가 설정되지 않았으므로
(즉, h1
과 h2
가 동점임),
커스텀 하이라이트의 스타일은
하이라이트 레지스트리에
등록된 순서대로 쌓입니다.
렌더링 결과는 "Som"이 노란색 배경의 파란색 글자,
"e t"가 오렌지 배경의 파란색 글자,
"ext"가 오렌지 배경의 기본 글자로 나타납니다.
h1
를 설정하면 h1
이 h2
보다 더 위에 쌓이므로,
"Some t"가 노란색 배경의 파란색 글자,
"ext"가 오렌지 배경의 기본 글자로 나타납니다.
4.2.6. 하이라이트 종류
커스텀 하이라이트의
type
속성은
하이라이트의 의미를 명확히 나타내기 위해 작성자가 지정할 수 있습니다.
이를 통해 보조 기술이 해당 하이라이트의 의미를
사용자에게 전달할 수 있습니다.
커스텀 하이라이트의 type
속성을 명시적으로 지정하지 않으면 기본값은 highlight
입니다.
참고: 작성자는 커스텀 하이라이트의 type
속성을 spelling-error
로
설정하여
해당 커스텀 하이라이트가 맞춤법 오류 강조에 사용될 때 의미를 표현할 수 있습니다.
커스텀 하이라이트의 type
속성을 grammar-error
로
설정하면
문법 오류 강조에 사용할 수 있습니다.
그 밖의 경우에는 type
을
highlight
로
두는 것이 좋습니다.
UA는 커스텀 하이라이트를 보조
기술에 제공해야 합니다.
플랫폼 접근성 API를 통해 하이라이트를 노출할 때,
UA는 해당 type
속성에 명시된 의미를
해당 접근성 API에서 가능한 한 구체적으로 표현해야 합니다.
참고: 예를 들어,
플랫폼 접근성 API가 맞춤법 오류와 문법 오류를 별도 표현할 수 있다면,
UA는 spelling-error
와
spelling-error
로
지정된 하이라이트에 해당 의미를 전달해야 합니다.
접근성 API가 맞춤법 오류만 표현할 수 있다면,
spelling-error
와
grammar-error
모두 맞춤법 오류 의미로 전달합니다.
만약 맞춤법/문법 오류 모두를 표현할 수 없다면,
모든 하이라이트를 highlight
로
노출합니다.
참고: 이 초기 타입 집합은
Highlight API의 대표적 사용 사례로 예상되는 것과
플랫폼 접근성 API에서 이미 일부 의미 표현이 가능한 것들로 선정되었습니다.
현재 접근성 API는 Highlight API의 기타 예상 사용 사례의 구체적 의미를 표현할 수 없습니다.
향후 접근성 API가 추가적인 Highlight API 사용 사례의 의미를 표현할 수 있게 되면,
HighlightType
에
더 많은 타입이 추가될 수 있습니다.
5. 변경 사항에 대응하기
5.1. 다시 그리기
하이라이트 레지스트리에 커스텀 하이라이트를 추가하거나 제거할 때, 또는 등록된 커스텀 하이라이트 내의 range를 추가하거나 제거할 때, 사용자 에이전트는 렌더링을 다시 평가하고 필요 시 다시 그려야 합니다.
사용자 에이전트는 또한 작성자가 priority
를
변경하거나,
boundary points를 Range
들의
등록된 커스텀 하이라이트에서 변경할 경우에도
필요에 따라 하이라이트를 다시 그려야 합니다.
이 재평가의 타이밍(동기/비동기)을 어떻게 명세해야 할까요? [Issue #4596]
5.2. Range 업데이트 및 무효화
작성자는 Range
또는 StaticRange
를
사용해 커스텀 하이라이트를 만들 수
있습니다.
생성된 커스텀 하이라이트는 문서의 동일한 부분을 나타내며, 동일하게 스타일링할 수 있습니다. 하지만, 기본 문서가 수정될 경우 동작은 달라집니다.
Range
는
라이브 range입니다.
사용자 에이전트는 Range
의
boundary points를
해당 range와 겹치거나 경계에서 DOM 변경이 발생하면 조정하고,
다시 그리기를 실행합니다. boundary points는 라이브 range에서 작성자가 직접 변경할 수도 있습니다.
반면, StaticRange
의
boundary points는
DOM 변경에 따라 사용자 에이전트가 조정하지 않으며,
생성 이후 작성자가 변경할 수도 없습니다.
사용자 에이전트는 실제 StaticRange
객체를 저장해야 하며,
라이브 Range
로
대체해서는 안 됩니다.
Range
객체를 업데이트하는 것은 성능에 큰 부담을 줍니다.
DOM 변경을 감지하고 이에 따라 커스텀
하이라이트 내의 range를 조정하거나 재생성하려는 작성자는
불필요한 비용을 피하기 위해 StaticRange
를
사용하는 것이 권장됩니다.
반대로 StaticRange
를
사용하는 작성자는 DOM 변경을 관찰하고,
오래된 range 또는 커스텀 하이라이트를 폐기하고 새로 만들어야 합니다.
문서를 렌더링하는 방법을 계산할 때,
해당 문서의 window에 연관된 하이라이트 레지스트리 내의 어떤 range의 start node 또는 end
node가
해당 문서가 아닌 Node
의
shadow-including root를 참조할 경우,
사용자 에이전트는 해당 range를 무시해야 합니다.
해당 문서의 window에 연관된 하이라이트 레지스트리 내의 어떤 StaticRange
가
유효하지 않은 경우,
사용자 에이전트는 해당 range를 무시해야 합니다.
[css-contain-2]가 적용된 커스텀 하이라이트 내의 StaticRange
의
상호작용은 문제를 일으킵니다:
완전히 포함된 요소에서는,
해당 요소의 하위에서 DOM 변경이 발생해도
포함된 요소 외부의 요소에 대해 무효화 및 재스타일/재페인팅이 발생하지 않아야 한다고 기대할 수 있습니다.
하지만 static range의 경계점이 포함된 서브트리 내부와 외부에 각각 있을 경우,
내부 경계점이 더 이상 유효한 노드를 가리키지 않게 되면,
전체 range가 무시되어야 하며,
이는 포함 서브트리 외부의 페인팅에도 영향을 미치게 됩니다.
이것은 스타일 포함의 취약점인지,
위의 무효화 논리의 문제인지,
아니면 다른 원인인지? [Issue #4598]
6. 이벤트 처리
이벤트 섹션은 https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/master/highlight/events-explainer.md 를 바탕으로 작성될 예정입니다.
커스텀 하이라이트에 전용 이벤트 처리 메커니즘을 도입해야 하는지, 아니면 의사요소 전반에 추가해야 하는지 검토가 필요합니다.
부록 A. 개인정보 및 보안 고려사항
이 섹션은 비규범적입니다.
이 명세는 새로운 보안 혹은 개인정보 문제를 야기하지 않는 것으로 보입니다. 만약 그렇지 않다고 의심되는 경우, CSS 워킹 그룹이나 공동 편집자에게 연락해 주시기 바랍니다.
부록 C. 변경 사항
이 섹션은 비규범적입니다.
2020년 12월 8일 워킹드래프트 이후 변경 사항
여러 편집적 개선 및 사소한 조정 외에, 주요 변경 사항은 다음과 같습니다:
-
HighlightsRegister
를HighlightRegistry
로 이름 변경 -
HighlightRegistry
에서 중복되는add()
메서드 삭제 (이슈 6092 참고) -
커스텀 하이라이트 오버레이가 네이티브 하이라이트 오버레이 아래에 쌓이도록 변경 (이슈 4595 참고)
-
하이라이트 우선순위를 실수 대신 정수로 처리하도록 변경 (이슈 4592 참고)
-
하이라이트 우선순위의 기본값을 0으로 정의 (이슈 6136 참고)
-
HighlightRegistry를 setlike에서 maplike로 변경하고, Highlight에서
name
속성 제거 (이슈 5910 참고) -
잘못된 window에서 나온 range는 페인팅되지 않음을 명확히 함 (이슈 6417 참고)
-
커스텀 하이라이트에는 UA 스타일이 없다는 점을 명시 (이슈 6375 참고)
-
type
속성을Highlight
에 추가하여 하이라이트의 의미를 명확하게 함. 접근성 도구에 하이라이트를 노출할 수 있도록 지원. (이슈 6498 참고)
2020년 10월 22일 워킹드래프트 이후 변경 사항
2020년 10월 22일 워킹드래프트 이후에는 편집적 변경만 있었습니다; 차이점을 참고하세요.