JavaScript 문서 객체 모델 Range

    2018-05-10 15:33:53 작성

    Range

    범위는 노드 경계에 구애 받지 않고 문서의 원하는 부분을 자유롭게 선택할 수 있습니다.
    물론 선택은 이면에서 일어나며 사용자는 이를 알지 못합니다.

    • 범위 생성

      새로운 범위를 생성하려면 document.createRange() 메서드를 사용하면됩니다.

      var range = document.createRange();

    • Range 객체

      속성 설명
      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()의 진짜 능력은 노드의 일부를 선택하는데 있습니다.

      setStart()범위지정

    • 범위의 콘텐츠 조작

      범위를 생성하면 해당범위를 포함하는 노드 내부에 문서 버퍼 노드가 생성됩니다.
      위의 예제에서 범위는 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로 감쌉니다. 다음 단계가 수행됩니다.
      1. 범위를 추출합니다.(예 : var contents = extractContents())
      2. newNode를 범위 위치에 삽입합니다. (range.insertNode(newNode))
      3. 추출한 콘텐츠를 newNode에 추가합니다. (newNode.appendChild(contents))

      newNode로 감싸려면 범위는 전체 노드여야 합니다.
      즉, startContainer, endContainer, commonAncestorContainer가 동일해야 합니다.

      insertNode() 예제

      surroundContents() 예제

    • 범위 접기

      범위가 어느부분도 선택되지 않았을때 접혀있다고 합니다.
      범위 접기

      속성/메서드 설명
      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의 비교지점이 sourceRange의 비교지점보다
      • 앞에 있으면 -1
      • 일치 하면 0
      • 뒤에 있으면 1
      을 반환합니다.

      위예제의 선택은 다음 그림과 같습니다.
      범위 비교

    • 범위 복제

      range.cloneRange()

      위 메서드로 범위를 복제할 수 있습니다.

    • 범위 정리

      범위로 할 일을 끝냈다면 메모리를 회수해야 합니다.
      detach() 메서드를 호출하거나 null 값을 대입하여 메모리를 해제할 수 있습니다.

      • range.detach()
      • range = null
    • 사용자 선택 범위

      window.getSelection()

      위의 메서드를 사용하면 사용자가 선택한 영역을 Selection 객체로 반환한다.
      Selection 객체의 getRangeAt() 메서드를 사용하면 Range 객체로 반환할 수 있다.