find.find()

在选项卡中搜索文本。

您可以使用此函数搜索普通的 HTTP(S) 网页。它搜索单个选项卡:您可以指定要搜索的特定选项卡的 ID,或者默认情况下它将搜索活动选项卡。它搜索选项卡中的所有框架。

您可以使搜索区分大小写,并使其仅匹配整个单词。

默认情况下,该函数仅返回找到的匹配项数。通过传入includeRangeDataincludeRectData选项,您可以获取有关目标选项卡中匹配项位置的更多信息。

此函数在内部存储结果,因此下次任何扩展调用find.highlightResults()时,将突出显示此 find 调用的结果,直到下次有人调用find()

这是一个返回Promise的异步函数。

语法

js
browser.find.find(
  queryphrase,       // string
  options            // optional object
)

参数

options 可选

object. 指定附加选项的对象。它可以采用以下任何属性,所有属性都是可选的

区分大小写

boolean. 如果为true,则搜索区分大小写。默认为false

整个单词

boolean. 仅匹配整个单词:因此“Tok”不会在“Tokyo”中匹配。默认为false

includeRangeData

boolean. 在响应中包含范围数据,这些数据描述在页面 DOM 的哪个位置找到匹配项。默认为false

includeRectData

boolean. 在响应中包含矩形数据,这些数据描述在渲染页面中的哪个位置找到匹配项。默认为false

matchDiacritics

boolean. 如果为true,则搜索区分重音字母及其基本字母。例如,当设置为true时,搜索“résumé”不会找到“resume”的匹配项。默认为false

tabId

integer. 要搜索的选项卡的 ID。默认为活动选项卡。

queryphrase

string. 要搜索的文本。

返回值

一个Promise,它将使用包含最多三个属性的对象来实现

计数

integer. 找到的结果数。

rangeData 可选

array. 如果在options参数中给出了includeRangeData,则将包含此属性。它作为RangeData对象的数组提供,每个匹配项一个。每个RangeData对象都描述在 DOM 树的哪个位置找到匹配项。例如,这将使扩展能够获取每个匹配项周围的文本,以便显示匹配项的上下文。

这些项目对应于rectData中给出的项目,因此rangeData[i]描述与rectData[i]相同的匹配项。

每个RangeData包含以下属性

endOffset

匹配项在其文本节点中结束位置的序数。

endTextNodePos

匹配项结束所在的文本节点的序数。

framePos

包含匹配项的框架的索引。0 对应于父窗口。请注意,rangeData数组中对象的顺序将与框架索引的顺序依次对齐:例如,rangeData对象的第一组的framePos将为 0,下一组的framePos将为 1,依此类推。

startOffset

匹配项在其文本节点中开始位置的序数。

startTextNodePos

匹配项开始所在的文本节点的序数。

rectData 可选

array. 如果在options参数中给出了includeRectData,则将包含此属性。它是一个RectData对象的数组。它包含搜索中所有匹配文本的客户端矩形,相对于视口的左上角。扩展可以使用它来提供对结果的自定义突出显示。

每个RectData对象都包含单个匹配项的矩形数据。它有两个属性

rectsAndTexts

一个包含两个属性的对象,这两个属性都是数组

  • rectList:一个对象的数组,每个对象都有四个整数属性:topleftbottomright。这些描述了相对于视口左上角的矩形。
  • textList:一个字符串数组,对应于rectList数组。textList[i]处的条目包含由rectList[i]处的矩形绑定的匹配项的一部分。

例如,考虑网页的一部分,如下所示

Text reading "this domain is established to be used for illustrative examples in documents. You may use this domain in examples without prior coordination or asking for permission." and a "More information" link.

如果您搜索“You may”,则匹配项需要由两个矩形描述

This domain is established to be used for illustrative examples in documents. You may use this domain in examples without prior coordination or asking for permission.". The words "you may" are highlighted.

在这种情况下,在描述此匹配项的RectData中,rectsAndTexts.rectListrectsAndTexts.textList将分别包含 2 个项目。

  • textList[0]将包含“You ”,而rectList[0]将包含其边界矩形。
  • textList[1]将包含“may”,而rectList[1]将包含其边界矩形。
文本

匹配项的完整文本,在上例中为“You may”。

浏览器兼容性

BCD 表仅在启用 JavaScript 的浏览器中加载。

示例

基本示例

在活动选项卡中搜索“banana”,记录匹配项数并突出显示它们

js
function found(results) {
  console.log(`There were: ${results.count} matches.`);
  if (results.count > 0) {
    browser.find.highlightResults();
  }
}

browser.find.find("banana").then(found);

在所有选项卡中搜索“banana”(请注意,这需要“选项卡”权限或匹配的主机权限,因为它访问tab.url

js
async function findInAllTabs(allTabs) {
  for (const tab of allTabs) {
    const results = await browser.find.find("banana", { tabId: tab.id });
    console.log(`In page "${tab.url}": ${results.count} matches.`);
  }
}

browser.tabs.query({}).then(findInAllTabs);

使用 rangeData

在此示例中,扩展使用rangeData获取找到匹配项的上下文。上下文是找到匹配项的节点的完整textContent。如果匹配项跨越节点,则上下文是所有跨越节点的textContent的连接。

请注意,为了简单起见,此示例未处理包含框架的页面。要支持此功能,您需要将rangeData拆分为每个框架一组,并在每个框架中执行脚本。

后台脚本

js
// background.js

async function getContexts(matches) {
  // get the active tab ID
  const activeTabArray = await browser.tabs.query({
    active: true,
    currentWindow: true,
  });
  const tabId = activeTabArray[0].id;

  // execute the content script in the active tab
  await browser.tabs.executeScript(tabId, { file: "get-context.js" });
  // ask the content script to get the contexts for us
  const contexts = await browser.tabs.sendMessage(tabId, {
    ranges: matches.rangeData,
  });
  for (const context of contexts) {
    console.log(context);
  }
}

browser.browserAction.onClicked.addListener((tab) => {
  browser.find.find("example", { includeRangeData: true }).then(getContexts);
});

内容脚本

js
/**
 * Get all the text nodes into a single array
 */
function getNodes() {
  const walker = document.createTreeWalker(
    document,
    window.NodeFilter.SHOW_TEXT,
    null,
    false,
  );
  const nodes = [];
  while ((node = walker.nextNode())) {
    nodes.push(node);
  }

  return nodes;
}

/**
 * Gets all text nodes in the document, then for each match, return the
 * complete text content of nodes that contained the match.
 * If a match spanned more than one node, concatenate the textContent
 * of each node.
 */
function getContexts(ranges) {
  const contexts = [];
  const nodes = getNodes();

  for (const range of ranges) {
    let context = nodes[range.startTextNodePos].textContent;
    let pos = range.startTextNodePos;
    while (pos < range.endTextNodePos) {
      pos++;
      context += nodes[pos].textContent;
    }
    contexts.push(context);
  }
  return contexts;
}

browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
  sendResponse(getContexts(message.ranges));
});

使用 rectData

在此示例中,扩展使用rectData通过在其边界矩形顶部添加黑色 DIV 来“涂黑”匹配项

Three search results with some texted redacted by black rectangles.

请注意,从很多方面来说,这是一种糟糕的页面涂抹方式。

后台脚本

js
// background.js

async function redact(matches) {
  // get the active tab ID
  const activeTabArray = await browser.tabs.query({
    active: true,
    currentWindow: true,
  });
  const tabId = activeTabArray[0].id;

  // execute the content script in the active tab
  await browser.tabs.executeScript(tabId, { file: "redact.js" });
  // ask the content script to redact matches for us
  await browser.tabs.sendMessage(tabId, { rects: matches.rectData });
}

browser.browserAction.onClicked.addListener((tab) => {
  browser.find.find("banana", { includeRectData: true }).then(redact);
});

内容脚本

js
// redact.js

/**
 * Add a black DIV where the rect is.
 */
function redactRect(rect) {
  const redaction = document.createElement("div");
  redaction.style.backgroundColor = "black";
  redaction.style.position = "absolute";
  redaction.style.top = `${rect.top}px`;
  redaction.style.left = `${rect.left}px`;
  redaction.style.width = `${rect.right - rect.left}px`;
  redaction.style.height = `${rect.bottom - rect.top}px`;
  document.body.appendChild(redaction);
}

/**
 * Go through every rect, redacting them.
 */
function redactAll(rectData) {
  for (const match of rectData) {
    for (const rect of match.rectsAndTexts.rectList) {
      redactRect(rect);
    }
  }
}

browser.runtime.onMessage.addListener((message) => {
  redactAll(message.rects);
});

扩展示例