Selection과 Range API로 텍스트 선택 및 조작하기

Range API: 텍스트 범위 정의와 이해

Range는 문서 내에서 선택된 부분을 나타내는 객체로, 두 개의 경계(시작점(startContainer,StartOffset)과 끝점(endContainer, endOffset))를 정의합니다.

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>Range 기본 사용</title>
</head>
<body>
  <div id="textBlock">
    안녕하세요, <em>여러분!</em> 환영합니다.
  </div>
  <script>
    const textBlock = document.getElementById('textBlock');
    const range = new Range();

    // "여러분! 환영" 선택: <em>부터 텍스트 노드 끝까지
    range.setStart(textBlock.childNodes[1], 0); // <em>의 첫 문자
    range.setEnd(textBlock.childNodes[2], 7);   // " 환영합니다"의 끝까지

    console.log('선택 범위:', range.toString()); // "여러분! 환영"
  </script>
</body>
</html>
  • setStart/setEnd: 특정 콘텐츠를 추출할 수 있는 범위 지정
  • toString(): 선택된 텍스트 반환

Range 조작: 선택 영역 변경

Range API는 선택된 콘텐츠를 조작하는 다양한 메서드를 제공합니다. 선택 영역을 삭제, 삽입, 감싸는 등의 작업을 지원합니다.

주요 메서드

  • deleteContents(): 범위 내용 삭제
  • extractContents(): 선택 영역 잘라내기
  • insertNode(): 노드 삽입
  • surroundContents(): 범위를 새 요소로 감쌈
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>Range로 하이라이트</title>
  <style>
    mark { background: #fff176; padding: 2px; }
  </style>
</head>
<body>
  <p id="article">오늘은 좋은 날씨가 이어지고 있습니다.</p>
  <button id="markBtn">하이라이트</button>
  <script>
    const article = document.getElementById('article');
    const markBtn = document.getElementById('markBtn');

    markBtn.addEventListener('click', () => {
      const range = new Range();
      range.setStart(article.childNodes[0], 4); // "좋은"부터
      range.setEnd(article.childNodes[0], 9);   // " 날씨"까지

      try {
        const mark = document.createElement('mark');
        range.surroundContents(mark);
        console.log('하이라이트 적용:', range.toString());
      } catch (e) {
        console.error('하이라이트 실패:', e.message);
      }
    });
  </script>
</body>
</html>
  • surroundContents: 메서드는 선택 영역이 완전한 노드 경계를 포함할 때만 동작합니다. 만약 일부만 포함되거나 중첩된 태그가 불완전하면 에러가 발생할 수 있으므로, 상황에 맞게 Range를 조정해야 합니다.

불완전 태그로 에러 발생

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>Range로 하이라이트</title>
  <style>
    mark { background: #fff176; padding: 2px; }
  </style>
</head>
<body>
  <p id="article">오늘은 <span>좋은</span> 날씨가 이어지고 있습니다.</p>
  <button id="markBtn">하이라이트</button>
  <script>
    const article = document.getElementById('article');
    const markBtn = document.getElementById('markBtn');

    markBtn.addEventListener('click', () => {
      const range = new Range();
      range.setStart(article.childNodes[1], 0); // <span> 시작
      range.setEnd(article.childNodes[2], 4);   // " 날씨"까지

      try {
        const mark = document.createElement('mark');
        const fragment = range.extractContents();
        mark.appendChild(fragment);
        range.insertNode(mark);
        //range.surroundContents(mark);
        console.log('하이라이트 적용:', range.toString());
      } catch (e) {
        console.error('하이라이트 실패:', e.message);
      }
    });
  </script>
</body>
</html>

Selection API – 현재 선택된 영역 가져오기

Selection 객체는 사용자가 현재 문서에서 선택한 영역을 나타냅니다. window.getSelection() 또는 document.getSelection()을 사용하면 Selection 객체를 얻을 수 있으며, 이 객체에는 최소한 하나의 Range가 포함됩니다.

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>Selection 복사</title>
  <style>
    #content { padding: 10px; border: 1px solid #ddd; }
    #copyBtn { margin-top: 10px; }
  </style>
</head>
<body>
  <div id="content">여기에서 텍스트를 선택하고 버튼을 눌러 복사하세요.</div>
  <button id="copyBtn">선택 복사</button>
  <script>
    const copyBtn = document.getElementById('copyBtn');

    copyBtn.addEventListener('click', () => {
      const selection = window.getSelection();
      if (selection.rangeCount > 0) {
        const selectedText = selection.toString();
        if (selectedText) {
          navigator.clipboard.writeText(selectedText)
            .then(() => alert('복사 완료: ' + selectedText))
            .catch(err => console.error('복사 실패:', err));
        } else {
          alert('선택된 텍스트가 없습니다.');
        }
      }
    });
  </script>
</body>
</html>

폼 컨트롤에서의 텍스트 선택

<input>과 <textarea>에서는 selectionStart, selectionEnd, setRangeText()로 선택 영역을 직접 제어할 수 있습니다.

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>폼 텍스트 조작</title>
  <style>
    textarea { width: 300px; height: 80px; padding: 5px; }
    button { margin-top: 5px; }
  </style>
</head>
<body>
  <textarea id="editor">안녕하세요, 텍스트를 선택해 보세요.</textarea>
  <button id="replaceBtn">선택 대체</button>
  <script>
    const editor = document.getElementById('editor');
    const replaceBtn = document.getElementById('replaceBtn');

    replaceBtn.addEventListener('click', () => {
      const start = editor.selectionStart;
      const end = editor.selectionEnd;

      if (start !== end) {
        editor.setRangeText('[대체됨]', start, end, 'select');
        console.log('대체된 범위:', start, end);
      } else {
        alert('텍스트를 먼저 선택하세요.');
      }
    });
  </script>
</body>
</html>