범위는 노드 경계에 구애 받지 않고 문서의 원하는 부분을 자유롭게 선택할 수 있습니다.
물론 선택은 이면에서 일어나며 사용자는 이를 알지 못합니다.
새로운 범위를 생성하려면 document.createRange() 메서드를 사용하면됩니다.
var range = document.createRange();
속성 | 설명 |
---|---|
range.startContainer | 범위가 시작하는 부분을 포함하고 있는 노드 |
range.startOffset |
startContainer에서 범위가 시작하는 지점의 offset startContainer가 TEXT_NODE 라면 offset은 문자의 개수이며, ELEMENT_NODE라면 자식노드의 인덱스 입니다. |
range.endContainer | 범위가 끝나는 부분을 포함하고 있는 노드 |
range.endOffset |
endContainer에서 범위가 끝나는 지점의 offset startOffset과 같은 규칙이 적용됩니다. |
range.commonAncestorContainer | startContainer와 endContainer가 속하는 조상노드 |
메서드 | 설명 |
---|---|
selectNode(node) | 요소를 포함한 전체 노드를 선택 (outerHTML 부분) |
selectNodeContents(node) | 요소의 하위 노드를 선택 (innerHTML 부분) |
위 예제를 보면 범위를 2개 생성하는데 각각 선택범위는 아래 그림과 같습니다.
selectNode()메서드 :
range1의 startContainer, endContainer, commonAncestorContainer 모두 document.body 입니다.
startOffset은 body의 NODE 컬렉션에서 주어진 인덱스 이며 1입니다.(0은 공백 TEXT_NODE)
endOffset은 선택된 노드가 1개뿐이기 때문에 2입니다.
selectNodeContents()메서드 :
range2의 startContainer, endContainer, commonAncestorContainer 모두 매개변수로 지정한 p#p1 입니다.
startOffset은 p#p1의 첫번째 노드에서 시작하므로 0입니다.
endOffset은 p#p1의 childNodes.length이며 이 예제에서는 2개의 노드(<b>와 TEXT_NODE)가 있으므로 2입니다.
다음의 Range 객체의 메서드를 사용하면 노드를 세밀히 정할 수 있습니다.
메서드 | 설명 |
---|---|
range.setStartBefore(refNode) |
범위의 시작지점이 refNode의 앞으로 지정되며, refNode가 첫번째 노드가 됩니다. |
range.setStartAfter(refNode) | 범위의 시작지점이 refNode의 다음으로 지정되며, refNode.nextSibling이 첫번째 노드가 됩니다. |
range.setEndBefore(refNode) | 범위의 마지막 지점이 refNode의 앞으로 지정되며, refNode.previousSibling이 마지막 노드가 됩니다. |
range.setEndAfter(refNode) | 범위의 마지막 지점이 refNode의 다음으로 지정되며, refNode가 마지막 노드가 됩니다. |
다음 메서드를 사용하면 범위를 보다 상세히 설정할 수 있습니다.
메서드 | 설명 |
---|---|
range.setStart(refNode, startOffset) | 범위의 시작 지점을 refNode, startOffset으로 지정합니다. |
range.setEnd(refNode, endOffset) | 범위의 마지막 지점을 refNode, endOffset으로 지정합니다. |
위의 예제는 selectNode()와 selectNodeContents()메서드를 흉내내보았습니다.
setStart()와 setEnd()의 진짜 능력은 노드의 일부를 선택하는데 있습니다.
범위를 생성하면 해당범위를 포함하는 노드 내부에 문서 버퍼 노드가 생성됩니다.
위의 예제에서 범위는 TEXT_NODE에서 시작해서 TEXT_NODE로 끝나기 때문에
Range API는 여닫는 태그가 없음을 인지하고 유효한 DOM 구조를 재구성하여 이후의 명령에 대비합니다.
문서 버퍼 노드는 다음과 같은 모습이 될 것입니다.
<p id="p1><b>He</b><b>llo</b> world!</p>
helloNode에는 b태그를 동적으로 추가 하여 나누게 되고,
worldNode의 TEXT_NODE도 두개로 나뉘어 wo와 rld!로 나뉘게 됩니다.
일단 범위를 생성하면 범위의 콘텐츠는 다양한 메서드로 조작이 가능합니다.
(범위의 문서 버퍼에 있는 노드는 모두 문서의 실제 노드를 가리키는 포인터일 뿐 입니다.)
메서드 | 설명 |
---|---|
range.deleteContents() | 범위를 삭제합니다. |
range.extractContents() | 범위를 삭제하고 DocumentFragment 객체를 반환합니다. |
range.cloneContents() | 범위를 복제하여 DocumentFragment 객체를 반환합니다. |
메서드 | 설명 |
---|---|
range.insertNode(newNode) | 범위의 앞에 newNode를 삽입합니다. |
range.surroundContents(newNode) |
콘텐츠를 newNode로 감쌉니다. 다음 단계가 수행됩니다.
newNode로 감싸려면 범위는 전체 노드여야 합니다. |
범위가 어느부분도 선택되지 않았을때 접혀있다고 합니다.
속성/메서드 | 설명 |
---|---|
range.collapsed | 범위가 접혀있는지 여부를 반환 |
range.collapse(bool) |
블리언 값을 매개변수로 받는데, true 이면 시작점으로 접습니다. false 이면 끝점으로 접습니다. |
range.compareBoundaryPoints(how, sourceRange)
위 메서드로 범위경계가 겹치는지 확인할 수 있습니다.
비교방법(how)은 다음 상수 중 하나입니다.
비교방법 | 설명 |
---|---|
range.START_TO_START | (0) - range의 시작점과 sourceRange의 시작점을 비교합니다. |
range.START_TO_END | (1) - range의 시작점과 sourceRange의 끝점을 비교합니다. |
range.END_TO_END | (2) - range의 끝점과 sourceRange의 끝점을 비교합니다. |
range.END_TO_START | (3) - range의 끝점과 sourceRange의 시작점을 비교합니다. |
위예제의 선택은 다음 그림과 같습니다.
range.cloneRange()
위 메서드로 범위를 복제할 수 있습니다.
범위로 할 일을 끝냈다면 메모리를 회수해야 합니다.
detach() 메서드를 호출하거나 null 값을 대입하여 메모리를 해제할 수 있습니다.
window.getSelection()
위의 메서드를 사용하면 사용자가 선택한 영역을 Selection 객체로 반환한다.
Selection 객체의 getRangeAt() 메서드를 사용하면 Range 객체로 반환할 수 있다.