Browsing Assist 크롬 익스텐션 - 2

홍범선·2024년 12월 25일
0

Browsing-Assist

목록 보기
2/8

✔️ 드래그한 문장에 형광펜 칠하기

📄 Selection API란?

웹 브라우저에서 사용자가 선택한 텍스트 정보를 알 수 있는 API입니다.

selection 객체를 얻기 위해선 window.getSelection() 메서드를 사용합니다.


📄 getRangeAt(0)

selection 객체을 얻었으면 사용자에 의해 선택된 텍스트의 시작과 끝을 나타냅니다.

🤔그러면 JavaScript는 범위를 어떻게 이해할까요?


예제를 보면서 이해해 봅시다.

사용자가 본문에 response ~ 응답 까지 드래그를 했다고 가정합니다.

Range객체를 출력해보겠습니다.

  • startContainer: 범위의 시작을 정의하는 DOM 노드입니다.
    • 이 경우 pm.response를 담고 있는 TEXT Node가 됩니다.
    • TEXT NodeHTML태그가 아닌 순수한 텍스트를 의미합니다.
    • <strong>pm.reponse</strong> 태그 내부의 텍스트를 나타냅니다.

  • startOffset: 해당 노드 내에서 시작 위치의 오프셋(인덱스) 입니다.
    • 쉽게 말해, 텍스트가 몇 번째 문자에서 시작하는지를 나타냅니다.
    • 0부터 시작해 3번째 인덱스에서 시작하는 것을 알 수 있습니다.

  • endContainer: 범위의 끝을 정의하는 DOM 노드입니다.
    • 이 경우 "는 응답 데이터와 관련된 정보를 제공한다."를 담고 있는 TEXT Node가 됩니다.
    • <p>는 응답 데이터와 관련된 정보를 제공한다.</p> 태그 내부의 텍스트를 나타냅니다.

  • endOffset: 해당 노드 내에서 마지막 위치의 오프셋(인덱스) 입니다.
    • 쉽게 말해, 텍스트가 몇 번째 문자에서 끝나는 지를 나타냅니다.
    • 띄어쓰기 포함 0번째에서 시작하여 3번째 문자에서 끝이 나지만 +1을 해준 값을 반환합니다.

정리하자면 JavaScriptstartContainer의 startOffset부터 endContainer의 endOffset까지의 모든 텍스트를 사용자가 지정한 범위로 이해합니다.


📄 아이디어

결론부터 말하자면

Range객체 범위에 있는 Text Node커스텀 태그를 추가하는 것입니다.


예제를 보면서 이해해 봅시다.

사용자가 DOM 이란 텍스트를 선택했다고 가정해봅시다.

해당 Text Node를 찾아 커스텀 태그 bee를 추가한 결과를 보여줍니다.

코드를 보면서 이해해보겠습니다.

function findTextNodesInRange(range) {
  let textNodes = [];

  function isTextNode(node) {
    return node.nodeType === Node.TEXT_NODE;
  }

  function isNonEmptyTextNode(node) {
    return !/^\s*$/.test(node.nodeValue);
  }

  function recurse(node) {
    if (
      isTextNode(node) &&
      range.intersectsNode(node) &&
      isNonEmptyTextNode(node)
    ) {
      textNodes.push(node);
    } else {
      node.childNodes.forEach(recurse);
    }
  }
  recurse(range.commonAncestorContainer);
  return textNodes.filter((node) => range.intersectsNode(node));
}

위 함수는 range 객체 안에 있는 Text Node를 찾는 함수입니다.

  • commonAncestorContainer : startContainerendContainer가 공유하는 가장 가까운 공통 조상 노드입니다.

    • 즉, 최상위 노드인 HTML부터 탐색하는 대신, 공통 조상 노드 부터 시작하면 더 효육적으로 빠르게 탐색할 수 있습니다.

  • range.intersectsNode: Range객체에서 특정 노드가 해당 Range객체와 겹치는 부분이 있는지 확인합니다.

    • 즉 범위 내에 있다면 true를 반환하고, 범위 밖에 있다면 false를 반환하게 됩니다.

정리하자면
1. 텍스트 노드인지
2. 범위 내에 있는지
3. 비어있지 않는지 체크를 합니다.
만족하면 배열에 저장해 둡니다.


이 과정을 기반으로 재귀적으로 탐색을 진행합니다.


이제 찾은 Text Node들에 커스텀 태그를 추가하기만 하면 됩니다.

코드를 통해 이해해 봅시다.

function highlightTextNodes(textNodes, range, color, colorh, data) {
  textNodes.forEach((node) => {
    const nodeRange = createNodeRange(node, range);
    const highlightBeeTag = createHighlightBeeTag(data.id, color);
    nodeRange.surroundContents(highlightBeeTag);
  });
  highLightHover(data.id, color, colorh, range);
}
  • Text Node에 대해 range객체를 만들어 줍니다.
    • range.selectNodeContents(해당 텍스트 노드)를 하게 되면 Range객체를 쉽게 만들 수 있습니다.

  • 커스텀 태그인 bee 생성합니다.
    • bee태그에는 CSS로 배경색이 적용되어 있습니다.

  • surroundContents 함수를 통해 텍스트 범위에 bee 태그를 감쌉니다.

이렇게 한다면 복잡한 구조를 가진 태그에서도 정상적으로 형광펜이 칠해질 것입니다.

profile
알고리즘 정리 블로그입니다.

0개의 댓글