CSS Custom Highlight API

_clai發表於2024-04-29

CSS Custom Highlight API

CSS Custom Highlight API
JavaScript 建立範圍並使用 CSS 定義樣式來設定文件中任意文字範圍的樣式
該 API 允許開發者透過 CSS 自定義屬性來設定文字的樣式, 並將其應用到文件中的任意文字範圍。
本質就是查詢所有文字節點,收集匹配內容的 Range, 最後作為引數構建 Highlight 物件, 但需要注意的是其並未產生新的節點


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0" />
    <title>CSS Custom Highlight API</title>
    <style>
      .text {
        overflow-wrap: break-word;
        word-break: keep-all;
        white-space: pre-wrap;
        text-wrap: wrap;

        &::highlight(my-custom-highlight) {
          background-color: hotpink;
        }
      }
    </style>
  </head>
  <body>
    <strong>CSS Custom Highlight API</strong>
    <hr />
    <div>
      <span>搜尋文字:&nbsp;</span>
      <input type="text" id="searchText" />
    </div>
    <p class="text">
      Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam nisi animi mollitia rem,
      placeat sint recusandae ab voluptatum, consequatur excepturi voluptatem repudiandae sed
      tempora, itaque optio distinctio ratione minus libero! Culpa eius, sint unde blanditiis
      deserunt molestiae beatae illum quidem quo alias modi aut adipisci in quia doloribus quisquam
      fugiat vero ut? Corrupti provident cupiditate velit quasi praesentium numquam ratione.
    </p>

    <script>
      // CSS Custom Highlight API
      // JavaScript 建立範圍並使用 CSS 定義樣式來設定文件中任意文字範圍的樣式
      // 該 API 允許開發者透過 CSS 自定義屬性來設定文字的樣式, 並將其應用到文件中的任意文字範圍。
      // 就是查詢所有文字節點,收集匹配內容的 Range, 最後作為引數構建 HighLight 物件, 但需要注意的是其並未產生新的節點

      const searchText = document.getElementById('searchText');
      const article = document.querySelector('.text');

      // 建立一個 TreeWalker 物件來遍歷文件中的所有文字節點
      const treeWalker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);
      // console.log('treeWalker => ', treeWalker);
      const allTextNodes = [];
      let currentNode = treeWalker.nextNode();
      while (currentNode) {
        allTextNodes.push(currentNode);
        currentNode = treeWalker.nextNode();
      }

      searchText.addEventListener('input', () => {
        // 清空高亮效果
        CSS.highlights.clear();

        const searchValue = searchText.value.trimStart().trimEnd();
        if (searchValue === '') return;

        const ranges = allTextNodes.map((node) => {
          const text = node.textContent;
          // 所匹配的位置
          const indices = [];
          // 記錄所有匹配的位置
          let startPos = 0;

          // 儲存所有匹配的位置
          while (startPos < text.length) {
            const index = text.indexOf(searchValue, startPos);
            if (index === -1) break;
            indices.push(index);
            startPos = index + searchValue.length;
          }

          return indices.map((index) => {
            // 構建一個 Range 物件
            const range = new Range();
            // 設定 Range 的開始和結束位置
            range.setStart(node, index);
            range.setEnd(node, index + searchValue.length);
            return range;
          });
        });
        // console.log('ranges => ', ranges);

        // 構建一個 Highlight 物件並應用到文件中
        const searchResults = new Highlight(...ranges.flat());

        // 高亮效果
        CSS.highlights.set('my-custom-highlight', searchResults);
      });
    </script>
  </body>
</html>

相關文章