成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

使用 GPT 寫代碼:高亮頁面關鍵字

人工智能
目前這種使用方式的效率還有待提升。整個過程耗費了 1 個多小時,寫了不到 100 行代碼。在對話的過程中,還需要將代碼復制到 IDE 中,這并沒有將 GPT 和 IDE 串聯起來。

最近用戶跟我反饋了一個問題,他們使用的瀏覽器高亮關鍵字的插件在我們的網站不生效,我看了一下,因為使用了 webComponent 但插件沒有進行兼容。他們說這個功能非常重要,因此,我們就要在系統內自己實現高亮頁面關鍵字。

在做一個新功能的時候,首先要先調研一下方案。

但在網上找了一圈,發現基本上都是以下這種方案:

// js部分
var bodyContent = document.body.innerHTMl;  // 獲取頁面內容
var pattern = new RegExp('關鍵字', 'g'); // 生成正則表達式
// 匹配關鍵詞并替換
document.body.innerHTMl = bodyContent.replace(pattern, '<span class="highlight">$&</span>');

在 innerHTML 匹配關鍵字,將關鍵字替換成高亮的 span,然后重新替換 innerHTML 這樣實現雖然非常簡單,但會導致 shadowRoot 和 iframe 失效。

于是我決定問問 GPT,看看它能不能給我一點驚喜

使用 GPT 編寫代碼

我:我要寫一段 js 代碼,傳入指定的文本,在整個頁面高亮

GPT: 你可以使用 JavaScript 中的 DOM 操作來實現這個功能。以下是一段示例代碼:

function highlightText(text) {
  const body = document.body;
  const regex = new RegExp(text, 'gi');
  let match;

  while ((match = regex.exec(body.innerText))) {
    const span = document.createElement('span');
    span.style.backgroundColor = 'yellow';
    span.textContent = match[0];
    const range = document.createRange();
    range.setStart(body, match.index);
    range.setEnd(body, regex.lastIndex);
    range.deleteContents();
    range.insertNode(span);
  }
}

這段代碼會將頁面中所有匹配指定文本的文本節點用黃色高亮顯示。你可以將需要高亮的文本作為參數傳入 highlightText 函數中,例如:

highlightText('JavaScript');

點評:代碼是寫出來了,用了一個我還沒見過的 API。。。趕緊學習了一下 原理是:用 Range API 去選擇對應的文本,然后使用 Range API 刪除和新增高亮節點,思路看起來是沒問題的,而且跟網上找到的不同

撤銷高亮

我: 還要能夠撤銷高亮

GPT: 好的,為了實現撤銷高亮的功能,我們可以在高亮文本時將每個被高亮的文本節點保存到一個數組中,然后在需要撤銷高亮時,遍歷這個數組,將每個高亮的文本節點恢復到原來的狀態。

以下是修改后的代碼:

let highlightedNodes = [];

function highlightText(text) {
  const body = document.body;
  const regex = new RegExp(text, 'gi');
  let match;

  while ((match = regex.exec(body.innerText))) {
    const span = document.createElement('span');
    span.style.backgroundColor = 'yellow';
    span.textContent = match[0];
    const range = document.createRange();
    range.setStart(body, match.index);
    range.setEnd(body, regex.lastIndex);
    range.deleteContents();
    range.insertNode(span);
    regex.lastIndex -= match[0].length - span.textContent.length;
    highlightedNodes.push(span);
  }
}

function clearHighlight() {
  while (highlightedNodes.length > 0) {
    const node = highlightedNodes.pop();
    const parent = node.parentNode;
    parent.replaceChild(node.firstChild, node);
    parent.normalize();
  }
}

點評:牛逼,GPT 還是很聰明的,居然還會把 span 存起來,用于撤銷高亮,這個思路也是非常的正確的。

Range 范圍算不準

但是實際運行后,這段代碼是會報錯的,我排查了一下,發現計算出來的 Range 的 start 和 end 的位置是錯誤的,于是我跟 GPT 說一下,看看它如何糾正

我:算不準 start 和 end 的位置

GPT: 如果 setStart 和 setEnd 方法的位置計算不準確,可能是因為在計算位置時沒有考慮到文本節點之間的空格和換行符。在這種情況下,你可以使用 createTreeWalker 方法來遍歷文檔樹中的所有文本節點,并計算出每個文本節點的起始和結束位置。

以下是一段示例代碼:

function highlightText(text) {
  const body = document.body;
  const regex = new RegExp(text, 'gi');
  let match;

  const walker = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null, false);
  while (walker.nextNode()) {
    const node = walker.currentNode;
    while ((match = regex.exec(node.nodeValue))) {
      const span = document.createElement('span');
      span.style.backgroundColor = 'yellow';
      span.textContent = match[0];
      const range = document.createRange();
      range.setStart(node, match.index);
      range.setEnd(node, regex.lastIndex);
      range.deleteContents();
      range.insertNode(span);
    }
  }
}

在這個修改后的代碼中,我們使用 createTreeWalker 方法來遍歷文檔樹中的所有文本節點。對于每個文本節點,我們使用 exec 方法來查找匹配指定文本的子串,并將每個匹配的子串用黃色高亮顯示。在計算位置時,我們使用 setStart 和 setEnd 方法來設置范圍的起始和結束位置,以確保高亮顯示的文本范圍正確。

點評:有點東西,我其實是不知道為什么算不準 start 和 end 的位置,但 GPT 自己也能發現問題,并且給出了另一個解決方案,并且非常貼心解析了這段代碼

根據 GPT 的提示,是空字符的文本節點導致的問題,跟 GPT 提出的假設類似。

頁面卡死

但我把上面代碼運行,發現頁面卡死了,原因是,在 TreeWalker 遍歷過程中,新增了高亮節點,然后又進入高亮節點進行遍歷,然后又創建高亮節點。。。

因此應該在 TreeWalker 遍歷節點之后再進行高亮節點替換,于是我讓 GPT 繼續改

我:在 TreeWalker 遍歷過程中修改節點,會遍歷到新節點,需要延遲修改節點

GPT: 是的,你說的很對。在 TreeWalker 遍歷過程中修改節點,會導致遍歷到新節點,從而可能會出現意外的結果。為了避免這種情況,我們可以將需要修改的節點保存到一個數組中,在遍歷完成后再對這些節點進行修改。

以下是一段示例代碼:

function highlightText(text) {
  const body = document.body;
  const regex = new RegExp(text, 'gi');
  let match;

  const nodesToHighlight = [];

  const walker = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null, false);
  while (walker.nextNode()) {
    const node = walker.currentNode;
    while ((match = regex.exec(node.nodeValue))) {
      const span = document.createElement('span');
      span.style.backgroundColor = 'yellow';
      span.textContent = match[0];
      const range = document.createRange();
      range.setStart(node, match.index);
      range.setEnd(node, regex.lastIndex);
      range.deleteContents();
      range.insertNode(span);
      nodesToHighlight.push(span);
    }
  }

  nodesToHighlight.forEach(node => {
    const parent = node.parentNode;
    parent.replaceChild(node.firstChild, node);
    parent.normalize();
  });
}

在這個修改后的代碼中,我們新增了一個名為 nodesToHighlight 的數組,用于保存需要高亮的節點。在遍歷過程中,每當找到一個需要高亮的節點時,我們將這個節點添加到 nodesToHighlight 數組中。在遍歷完成后,我們遍歷 nodesToHighlight 數組,對每個節點進行高亮處理。

這樣做可以確保在遍歷過程中不會修改節點,從而避免出現意外的結果。

點評:改了,但沒完全改好,TreeWalker 中仍然有修改 DOM 的操作,于是我換了個說法

我:不能在遍歷過程中調用 deleteContents 和 insertNode

GPT: 是的,你說的很對。在遍歷過程中調用 deleteContents 和 insertNode 方法,會導致遍歷到新節點,從而可能會出現意外的結果。為了避免這種情況,我們可以將需要修改的節點保存到一個數組中,在遍歷完成后再對這些節點進行修改。

以下是一段示例代碼:

function highlightText(text) {
  const body = document.body;
  const regex = new RegExp(text, 'gi');
  let match;

  const nodesToHighlight = [];

  const walker = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, {
    acceptNode: function(node) {
      if (node.parentNode.nodeName.match(/^(script|style)$/i)) {
        return NodeFilter.FILTER_REJECT;
      }
      return NodeFilter.FILTER_ACCEPT;
    }
  }, false);

  while (walker.nextNode()) {
    const node = walker.currentNode;
    while ((match = regex.exec(node.nodeValue))) {
      const span = document.createElement('span');
      span.style.backgroundColor = 'yellow';
      span.textContent = match[0];
      const range = document.createRange();
      range.setStart(node, match.index);
      range.setEnd(node, regex.lastIndex);
      nodesToHighlight.push({ range, span });
      regex.lastIndex -= match[0].length - span.textContent.length;
    }
  }

  setTimeout(() => {
    nodesToHighlight.forEach(({ range, span }) => {
      range.deleteContents();
      range.insertNode(span);
    });
  }, 0);
}

在這個修改后的代碼中,我們新增了一個名為 nodesToHighlight 的數組,用于保存需要高亮的節點和它們對應的范圍。在遍歷過程中,每當找到一個需要高亮的節點時,我們將這個節點和它對應的范圍添加到 nodesToHighlight 數組中。在遍歷完成后,我們使用 setTimeout 方法將對節點的修改延遲到下一個事件循環中執行。

這樣做可以確保在遍歷過程中不會修改節點,從而避免出現意外的結果。同時,使用 setTimeout 方法可以將對節點的修改延遲到下一個事件循環中執行,從而確保在修改節點時不會影響遍歷過程。

點評:這段代碼終于能運行了,非常的優秀!

支持 shadowRoot

我:但這樣還不能遍歷 webComponent shadowRoot 里面的元素

GPT:

function highlightText(text) {
  const body = document.body;
  const regex = new RegExp(text, 'gi');
  let match;

  const nodesToHighlight = [];

  const walker = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, {
    acceptNode: function(node) {
      if (node.parentNode.nodeName.match(/^(script|style)$/i)) {
        return NodeFilter.FILTER_REJECT;
      }
      return NodeFilter.FILTER_ACCEPT;
    }
  }, false);

  while (walker.nextNode()) {
    const node = walker.currentNode;
    while ((match = regex.exec(node.nodeValue))) {
      const span = document.createElement('span');
      span.style.backgroundColor = 'yellow';
      span.textContent = match[0];
      const range = document.createRange();
      range.setStart(node, match.index);
      range.setEnd(node, regex.lastIndex);
      nodesToHighlight.push({ range, span });
      regex.lastIndex -= match[0].length - span.textContent.length;
    }
  }

  const shadowHosts = document.querySelectorAll('*');
  shadowHosts.forEach(shadowHost => {
    const shadowRoot = shadowHost.shadowRoot;
    if (shadowRoot) {
      const walker = document.createTreeWalker(shadowRoot, NodeFilter.SHOW_TEXT, {
        acceptNode: function(node) {
          if (node.parentNode.nodeName.match(/^(script|style)$/i)) {
            return NodeFilter.FILTER_REJECT;
          }
          return NodeFilter.FILTER_ACCEPT;
        }
      }, true);

      while (walker.nextNode()) {
        const node = walker.currentNode;
        while ((match = regex.exec(node.nodeValue))) {
          const span = document.createElement('span');
          span.style.backgroundColor = 'yellow';
          span.textContent = match[0];
          const range = document.createRange();
          range.setStart(node, match.index);
          range.setEnd(node, regex.lastIndex);
          nodesToHighlight.push({ range, span });
        }
      }
    }
  });

  setTimeout(() => {
    nodesToHighlight.forEach(({ range, span }) => {
      range.deleteContents();
      range.insertNode(span);
    });
  }, 0);
}

在這個修改后的代碼中,我們使用 querySelectorAll 方法來獲取所有元素,然后對于每個元素,我們**檢查它是否有 shadowRoot**。如果有,我們使用 createTreeWalker 方法來遍歷它的 shadowRoot 中的所有文本節點。在遍歷過程中,我們使用與之前相同的方式來查找需要高亮的節點,并將它們添加到 nodesToHighlight 數組中。

這樣做可以確保遍歷到 shadowRoot 中的所有文本節點,并將需要高亮的節點保存到 nodesToHighlight 數組中。然后,我們使用 setTimeout 方法將對節點的修改延遲到下一個事件循環中執行,從而確保在修改節點時不會影響遍歷過程。

點評:這段代碼也是能運行的,很牛逼,不過沒考慮代碼的復用性,shadowRoot 的處理,可以復用普通的 DOM 的高亮邏輯

撤銷操作支持 shadowRoot

我:撤銷操作也要作用到 shadowRoot

GPT: 是的,你說的很對。如果需要對 shadowRoot 中的高亮文本進行撤銷操作,我們可以使用 Range 對象的 deleteContents() 方法來刪除高亮文本所在的節點,并將原始文本重新插入到文檔中。

以下是一段示例代碼:

function removeHighlight() {
  const highlightedSpans = document.querySelectorAll('span[style="background-color: yellow;"]');
  highlightedSpans.forEach(span => {
    const range = document.createRange();
    range.selectNode(span);
    range.deleteContents();
    range.insertNode(document.createTextNode(span.textContent));
  });

  const shadowHosts = document.querySelectorAll('*');
  shadowHosts.forEach(shadowHost => {
    if (shadowHost.shadowRoot) {
      const highlightedSpans = shadowHost.shadowRoot.querySelectorAll('span[style="background-color: yellow;"]');
      highlightedSpans.forEach(span => {
        const range = document.createRange();
        range.selectNode(span);
        range.deleteContents();
        range.insertNode(document.createTextNode(span.textContent));
      });
    }
  });
}

點評:代碼可以運行,但有以下缺點:

  • ? 同樣是復用性不足,
  • ? 高亮節點的選擇器 span[style="background-color: yellow;"] 不太優雅,不夠健壯 但問題不大,我自己能改hhh

highligh 函數需要支持重復調用

目前的代碼會有一個問題,就是重復調用 highlightText,已高亮的節點,仍然會創建 span,這就會導致 span 重復嵌套。需要先判斷是否已經高亮了。

我:highlightText 如果同一個值多次調用,會給文本節點再嵌套一層高亮節點,要支持重復調用不會重復嵌套節點

GPT: 好的,如果需要支持重復調用 highlightText 函數時不會重復嵌套高亮節點,我們可以在每次調用函數之前先檢查文本節點是否已經被高亮過,如果已經被高亮過,則不再重復嵌套高亮節點。

以下是修改后的 highlightText 函數代碼:

function highlightText(text) {
  const body = document.body;
  const regex = new RegExp(text, 'gi');
  let match;

  const nodesToHighlight = [];

  const walker = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, {
    acceptNode: function(node) {
      if (node.parentNode.nodeName.match(/^(script|style)$/i)) {
        return NodeFilter.FILTER_REJECT;
      }
      return NodeFilter.FILTER_ACCEPT;
    }
  }, false);

  while (walker.nextNode()) {
    const node = walker.currentNode;
    if (node.parentNode.classList.contains('highlighted')) {
      continue;
    }
    while ((match = regex.exec(node.nodeValue))) {
      const span = document.createElement('span');
      span.style.backgroundColor = 'yellow';
      span.textContent = match[0];
      const range = document.createRange();
      range.setStart(node, match.index);
      range.setEnd(node, regex.lastIndex);
      nodesToHighlight.push({ range, span });
      regex.lastIndex -= match[0].length - span.textContent.length;
    }
  }

  const shadowHosts = document.querySelectorAll('*');
  shadowHosts.forEach(shadowHost => {
    if (shadowHost.shadowRoot) {
      const shadowRoot = shadowHost.shadowRoot;
      const walker = document.createTreeWalker(shadowRoot, NodeFilter.SHOW_TEXT, {
        acceptNode: function(node) {
          if (node.parentNode.nodeName.match(/^(script|style)$/i)) {
            return NodeFilter.FILTER_REJECT;
          }
          return NodeFilter.FILTER_ACCEPT;
        }
      }, true);

      while (walker.nextNode()) {
        const node = walker.currentNode;
        if (node.parentNode.classList.contains('highlighted')) {
          continue;
        }
        while ((match = regex.exec(node.nodeValue))) {
          const span = document.createElement('span');
          span.style.backgroundColor = 'yellow';
          span.textContent = match[0];
          const range = document.createRange();
          range.setStart(node, match.index);
          range.setEnd(node, regex.lastIndex);
          nodesToHighlight.push({ range, span });
          regex.lastIndex -= match[0].length - span.textContent.length;
        }
      }
    }
  });

  nodesToHighlight.forEach(nodeToHighlight => {
    const range = nodeToHighlight.range;
    const span = nodeToHighlight.span;
    range.deleteContents();
    span.classList.add('highlighted');
    range.insertNode(span);
  });
}

在這個代碼中,我們添加了一個名為 highlighted 的 CSS 類,用于標記已經被高亮過的文本節點。在每次遍歷文本節點時,我們檢查當前節點的父節點是否已經被標記為 highlighted,如果是,則跳過當前節點,不再重復嵌套高亮節點。在插入高亮節點時,我們還將其添加到 highlighted 類中,以便下次遍歷時可以正確地跳過已經被高亮過的節點。

點評:完美符合要求~我只需要稍微復用以下代碼,進行一些小修改即可。

最終還是成為了一名代碼美化工程師~~~

使用體驗

GPT 確實非常強大,它知道的非常多,有我不知道的知識,如果不是 GPT 我的確不會知道可以使用 Range API 去操作 DOM 實現高亮。

它還能較好的根據提示,糾正自己的錯誤

我們可以讓 GPT 提供思路,寫一個簡單的 Demo,然后自己進行代碼優化。

目前這種使用方式的效率還有待提升。整個過程耗費了 1 個多小時,寫了不到 100 行代碼。在對話的過程中,還需要將代碼復制到 IDE 中,這并沒有將 GPT 和 IDE 串聯起來。

不過,我們可以嘗使用 Cursor 這個 IDE,它可以直接在 IDE 中使用 GPT,這樣應該會有更好的編碼體驗。

責任編輯:武曉燕 來源: Candy 的修仙秘籍
相關推薦

2021-02-01 13:10:07

Staticc語言UNIX系統

2012-06-02 00:53:39

Javafinally

2010-07-23 14:32:43

SQL Server

2011-06-27 17:32:20

2009-09-17 09:30:00

Linq LET關鍵字

2022-01-04 16:35:42

C++Protected關鍵字

2009-09-02 09:24:03

C# this關鍵字

2012-03-01 12:50:03

Java

2009-08-21 14:58:56

C# this關鍵字

2013-01-30 10:12:14

Pythonyield

2018-04-20 15:56:09

Pythonglobal關鍵字

2009-12-08 18:02:06

PHP final關鍵

2009-06-25 10:33:53

StaticJava類

2009-06-29 18:14:23

Java多線程volatile關鍵字

2009-09-01 15:25:04

C# default關

2022-02-17 08:31:38

C語言staic關鍵字

2022-05-06 08:32:40

Pythonwith代碼

2022-06-29 08:05:25

Volatile關鍵字類型

2019-11-06 10:36:43

MavenoptionalJava

2009-08-13 13:04:29

C# lock關鍵字
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产视频黄色 | 天堂久久久久久久 | 午夜精品久久久 | 国产美女久久 | 午夜视频免费在线观看 | 日韩欧美在线一区二区 | 国产精品久久久久久妇女 | 国产精品国产三级国产播12软件 | 99精品欧美一区二区三区 | 久久成人免费视频 | 午夜理伦三级理论三级在线观看 | 亚洲视频手机在线 | 365夜爽爽欧美性午夜免费视频 | 国产精品久久久久一区二区三区 | 91视视频在线观看入口直接观看 | 日韩福利 | 99小视频| 亚洲精品91 | 在线国产99 | 日韩午夜在线播放 | 国产精品成人一区二区 | 久久国产精品视频 | 欧美aaaaaaaaaa| 伊人久久精品 | 99亚洲视频| 欧美一区二区三区大片 | 一区二区三区亚洲精品国 | 久久亚洲一区二区三区四区 | 久久精品欧美一区二区三区不卡 | 中文字幕国产精品 | 成人av在线播放 | 91精品国产色综合久久不卡98 | 国产精品久久精品 | 久久婷婷国产 | 欧美视频免费在线 | 亚洲一区在线日韩在线深爱 | 色婷婷精品久久二区二区蜜臂av | 欧洲在线视频 | 伊人精品| 免费在线观看成人 | 精品福利一区 |