文件:caretPositionFromPoint() 方法

可用性有限

此特性不是基線特性,因為它在一些最廣泛使用的瀏覽器中不起作用。

Document 介面的 caretPositionFromPoint() 方法返回一個 CaretPosition 物件,其中包含 DOM 節點以及該節點內插入符號和插入符號的字元偏移量。

語法

js
caretPositionFromPoint(x, y)
caretPositionFromPoint(x, y, options)

引數

x

點的水平座標。

y

點的垂直座標。

options 可選

還可以指定以下可選屬性。

shadowRoots 可選

ShadowRoot 物件的陣列。該方法可以返回一個在所提供的 shadow root 的 shadow DOM 中定義的節點的插入符號位置。如果插入符號位置落在未提供的 shadow root 中,則返回的 CaretPosition 將重新對映到作為 shadow root 主機的節點。

返回值

一個 CaretPosition 物件或 null

如果文件沒有關聯的視口,如果 xy 為負值或超出視口區域,或者如果座標指示的點無法插入文字插入點指示器,則返回值為 null

示例

在 DOM 中插入符號位置處拆分文字節點

此示例演示如何從選定的 DOM 節點獲取插入符號位置,使用該位置拆分節點,並在兩個節點之間插入換行符。該示例使用 caretPositionFromPoint()(如果支援)獲取插入符號位置,並以非標準的 Document.caretRangeFromPoint() 方法作為備用。

請注意,程式碼的某些部分是隱藏的,包括用於日誌記錄的程式碼,因為這對於理解此方法沒有用。

HTML

HTML 定義了一個段落文字。

html
<p>
  Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
  eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
  voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita
  kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>

JavaScript

下面的方法首先檢查 document.caretPositionFromPoint 支援,並使用它獲取插入符號位置的文字節點和偏移量。如果瀏覽器不支援該方法,則程式碼會檢查 document.caretRangeFromPoint,並改用它。

如果插入符號位置處的節點是文字節點,則程式碼會在選定的偏移量處將該節點拆分為兩個,並在兩個節點之間插入換行符。

js
function insertBreakAtPoint(e) {
  let range;
  let textNode;
  let offset;

  if (document.caretPositionFromPoint) {
    range = document.caretPositionFromPoint(e.clientX, e.clientY);
    textNode = range.offsetNode;
    offset = range.offset;
  } else if (document.caretRangeFromPoint) {
    // Use WebKit-proprietary fallback method
    range = document.caretRangeFromPoint(e.clientX, e.clientY);
    textNode = range.startContainer;
    offset = range.startOffset;
  } else {
    // Neither method is supported, do nothing
    return;
  }

  // Logging code (uses hidden method to get substring with ^ at offset)
  if (textNode?.nodeType === 3) {
    const caretInText = getSubstringAroundOffset(textNode.textContent, offset);
    log(
      `node: ${textNode.nodeName}, offset: ${offset}, insert: ${caretInText}`,
    );
  }

  // Only split TEXT_NODEs
  if (textNode?.nodeType === 3) {
    let replacement = textNode.splitText(offset);
    let br = document.createElement("br");
    textNode.parentNode.insertBefore(br, replacement);
  }
}

該方法被新增為任何段落元素的點選事件處理程式。

js
const paragraphs = document.getElementsByTagName("p");
for (const paragraph of paragraphs) {
  paragraph.addEventListener("click", insertBreakAtPoint);
}

結果

單擊下面 Lorem ipsum ... 段落中的任意位置,可在您單擊的點插入換行符。請注意,日誌顯示 nodeName、偏移量以及選定節點的片段,其中在偏移量處帶有 ^ 字元。

在 Shadow DOM 中在插入符號位置處拆分文字節點

此示例演示如何從 shadow root 中的選定節點獲取插入符號位置。該示例與上面的純 DOM 示例非常相似,不同之處在於部分文字位於 shadow root 內部。我們提供了一個按鈕,允許您檢視在將 shadow root 傳遞/未傳遞給 caretPositionFromPoint() 時之間的區別。

請注意,程式碼的某些部分是隱藏的,包括用於日誌記錄的程式碼,因為這對於理解此方法沒有用。

HTML

HTML 定義了一個 <div> 元素內部的段落文字。該段落包含一個 <span> 元素,其 id 為“host”,我們將用作 shadow root 的主機。還有一些按鈕,我們將用它們來重置示例,並向 caretPositionFromPoint() 新增/刪除 shadow root 選項引數。

html
<button id="reset" type="button">Reset</button>
<button id="shadowButton" type="button">Add Shadow</button>
<div>
  <p>
    Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
    eirmod tempor invidunt ut <span id="host"></span> labore et dolore magna
    aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo
    dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est
    Lorem ipsum dolor sit amet.
  </p>
</div>

CSS

在這裡,我們使用 CSS 使 #host 元素變為紅色和粗體。這使得區分 DOM 中的文字和 shadow DOM 中的文字更容易。

css
#host {
  color: red;
  font-weight: bold;
}

JavaScript

首先,我們有一些程式碼來填充我們的 shadow DOM。我們正在使用 JavaScript 動態附加 shadow root,因為 MDN 示例系統不允許我們使用 <template> 元素宣告性地執行此操作。shadow DOM 的內容是一個 <span> 元素,其中包含文字“I'm in the shadow DOM”。

js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const shadowSpan = document.createElement("span");
shadowSpan.textContent = "I'm in the shadow DOM";
shadow.appendChild(shadowSpan);

接下來,我們為“啟用/停用陰影”按鈕新增一個處理程式。此程式碼切換 useShadows 變數的值並相應地更新按鈕文字。

js
let useShadows = false;

const shadowButton = document.querySelector("#shadowButton");
shadowButton.addEventListener("click", () => {
  useShadows = !useShadows;
  shadowButton.innerText = useShadows ? "Remove Shadow" : "Add Shadow";
});

下面的方法首先檢查 document.caretPositionFromPoint 支援,並使用它獲取插入符號位置的文字節點和偏移量。useShadows 變數的值用於確定是否將我們文字中託管的 shadow root 傳遞給 caretPositionFromPoint()

  • 如果瀏覽器不支援該方法,則程式碼會檢查 document.caretRangeFromPoint,並改用它。
  • 如果插入符號位置處的節點是文字節點,則程式碼會在選定的偏移量處拆分節點,並在它們之間插入換行符。
  • 如果節點是元素節點,則程式碼會在偏移量處插入一個換行符元素節點。
js
function insertBreakAtPoint(e) {
  let range;
  let textNode;
  let offset;

  if (document.caretPositionFromPoint) {
    range = document.caretPositionFromPoint(
      e.clientX,
      e.clientY,
      useShadows ? { shadowRoots: [shadow] } : null,
    );
    textNode = range.offsetNode;
    offset = range.offset;
  } else if (document.caretRangeFromPoint) {
    // Use WebKit-proprietary fallback method
    range = document.caretRangeFromPoint(e.clientX, e.clientY);
    textNode = range.startContainer;
    offset = range.startOffset;
  } else {
    // Neither method is supported, do nothing
    return;
  }

  // Logging code (uses hidden method to get substring with ^ at offset)
  if (textNode) {
    if (textNode.nodeType === 3) {
      const caretInText = getSubstringAroundOffset(
        textNode.textContent,
        offset,
      );
      log(
        `type: TEXT_NODE, name: ${textNode.nodeName}, offset: ${offset}:
${caretInText}`,
      );
    } else if (textNode.nodeType === 1) {
      log(`type: ELEMENT_NODE, name: ${textNode.nodeName}, offset: ${offset}`);
    } else {
      log(
        `type: ${textNode.nodeType}, name: ${textNode.nodeName}, offset: ${offset}`,
      );
    }
  }

  // Insert line at caret
  if (textNode?.nodeType === 3) {
    // TEXT_NODE - split text at offset and add br
    let replacement = textNode.splitText(offset);
    let br = document.createElement("br");
    textNode.parentNode.insertBefore(br, replacement);
  } else if (textNode?.nodeType === 1) {
    // ELEMENT_NODE - Add br node at offset node
    let br = document.createElement("br");
    const targetNode = textNode.childNodes[offset];
    textNode.insertBefore(br, targetNode);
  } else {
    // Do nothing
  }
}

最後,我們分別為 DOM 和 shadow root 中的段落元素新增兩個點選事件處理程式。請注意,我們需要專門查詢 shadowRoot 中的元素,因為它們對正常的 DOM 查詢方法不可見。

js
// Click event handler <p> elements in the DOM
const paragraphs = document.getElementsByTagName("p");
for (const paragraph of paragraphs) {
  paragraph.addEventListener("click", insertBreakAtPoint);
}

// Click event handler <p> elements in the Shadow DOM
const shadowParagraphs = host.shadowRoot.querySelectorAll("p");
for (const paragraph of shadowParagraphs) {
  console.log(paragraph);
  paragraph.addEventListener("click", insertBreakAtPoint);
}

結果

單擊 shadow DOM 文字之前或之後 Lorem ipsum ... 段落中的任意位置,可在您單擊的點插入換行符。請注意,在這種情況下,日誌顯示您已選擇 TEXT_NODE、偏移量以及選定節點的片段,其中在偏移量處帶有 ^ 字元。

最初,shadow root 未傳遞給 caretPositionFromPoint(),因此如果您單擊文字“I'm in the shadow DOM”,返回的插入符號位置節點是主機的父節點,位於 shadow root 的偏移量處。因此,換行符被新增到節點之前,而不是您選擇的點。請注意,在這種情況下,插入符號位置節點的型別為 ELEMENT_NODE

如果您單擊“新增陰影”按鈕,則 shadow root 將傳遞給 caretPositionFromPoint(),因此返回的插入符號位置是 shadow DOM 中特定的選定節點。這使得 shadow DOM 文字的行為與其他段落文字類似。

規範

規範
CSSOM 檢視模組
# dom-document-caretpositionfrompoint

瀏覽器相容性

另見