Document: caretPositionFromPoint() Methode

Limited availability

This feature is not Baseline because it does not work in some of the most widely-used browsers.

Die caretPositionFromPoint()-Methode des Document-Interfaces gibt ein CaretPosition-Objekt zurück, das das DOM-Element sowie den Cursor und dessen Zeichenoffset in diesem Element enthält.

Syntax

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

Parameter

x

Die horizontale Koordinate eines Punktes.

y

Die vertikale Koordinate eines Punktes.

options Optional

Die folgenden optionalen Eigenschaften können ebenfalls angegeben werden.

shadowRoots Optional

Ein Array von ShadowRoot-Objekten. Die Methode kann eine Cursorposition für ein Element zurückgeben, das innerhalb des Shadow-DOM eines angegebenen Shadow-Roots definiert ist. Wenn sich die Cursorposition innerhalb eines nicht angegebenen Shadow-Roots befindet, wird die zurückgegebene CaretPosition auf das Element abgebildet, das der Host des Shadow-Roots ist.

Rückgabewert

Ein CaretPosition-Objekt oder null.

Der zurückgegebene Wert ist null, falls kein Viewport mit dem Dokument verknüpft ist, wenn x oder y negativ oder außerhalb des Viewport-Bereichs liegen, oder wenn die Koordinaten einen Punkt angeben, an dem kein Textcursor-Indikator eingefügt werden kann.

Beispiele

Textknoten an Cursorposition im DOM teilen

Dieses Beispiel zeigt, wie man die Cursorposition von einem ausgewählten DOM-Knoten erhält, die Position verwendet, um den Knoten zu teilen, und einen Zeilenumbruch zwischen den beiden Knoten einfügt. Das Beispiel nutzt caretPositionFromPoint(), um die Cursorposition zu erhalten, falls unterstützt, mit der nicht standardmäßigen Methode Document.caretRangeFromPoint() als Fallback.

Beachten Sie, dass einige Teile des Codes ausgeblendet sind, einschließlich Code für das Protokollieren, da dies zum Verständnis dieser Methode nicht erforderlich ist.

HTML

Das HTML definiert einen Absatz mit Text.

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

Die Methode prüft zunächst die Unterstützung von document.caretPositionFromPoint und verwendet diese, um den Textknoten und den Offset an der Cursorposition zu erhalten. Wenn der Browser diese Methode nicht unterstützt, prüft der Code dann auf document.caretRangeFromPoint und verwendet stattdessen diese.

Wenn der Knoten an der Cursorposition ein Textknoten ist, teilt der Code den Knoten an dem gewählten Offset und fügt einen Zeilenumbruch dazwischen.

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);
  }
}

Die Methode wird dann als Klick-Ereignishandler für alle Absatz-Elemente hinzugefügt.

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

Ergebnisse

Klicken Sie irgendwo im Lorem ipsum ... Absatz unten, um an der Stelle, an der Sie klicken, einen Zeilenumbruch einzufügen. Beachten Sie, dass das Protokoll den nodeName, den Offset und einen Ausschnitt des ausgewählten Knotens mit einem ^ Zeichen bei dem Offset zeigt.

Textknoten an Cursorpositionen in einem Shadow-DOM teilen

Dieses Beispiel zeigt, wie man die Cursorposition von einem ausgewählten Knoten innerhalb eines Shadow-Roots erhält. Das Beispiel ist dem oben beschriebenen DOM-nur Beispiel sehr ähnlich, außer dass ein Teil des Textes innerhalb eines Shadow-Roots liegt. Wir bieten eine Schaltfläche an, mit der Sie den Unterschied sehen können, wenn ein Shadow-Root an caretPositionFromPoint() übergeben wird oder nicht.

Beachten Sie, dass einige Teile des Codes ausgeblendet sind, einschließlich Code für das Protokollieren, da dies zum Verständnis dieser Methode nicht erforderlich ist.

HTML

Das HTML definiert einen Absatz von Text innerhalb eines <div> Elements. Der Absatz enthält ein <span> Element mit der id "host", das wir als Host für einen Shadow-Root verwenden. Es gibt auch einige Schaltflächen, die wir verwenden werden, um das Beispiel zurückzusetzen und das Shadow-Root-Optionsargument zu caretPositionFromPoint() hinzuzufügen/zu entfernen.

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

Hier verwenden wir CSS, um das #host Element rot und fett zu machen. Dies erleichtert die Unterscheidung zwischen Text im DOM und Text im Shadow-DOM.

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

JavaScript

Zuerst haben wir etwas Code, um unser Shadow-DOM zu befüllen. Wir verwenden JavaScript, um ein Shadow-Root dynamisch anzuhängen, da das MDN-Beispielsystem nicht erlaubt, dies deklarativ mit dem <template> Element zu tun. Der Inhalt des Shadow-DOM ist ein <span> Element, das den Text "I'm in the shadow DOM" enthält.

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);

Als nächstes fügen wir einen Handler für unsere "Enable/Disable shadow" Schaltfläche hinzu. Dieser Code schaltet den Wert der Variablen useShadows um und aktualisiert den Schaltflächentext entsprechend.

js
let useShadows = false;

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

Die Methode prüft zunächst die Unterstützung von document.caretPositionFromPoint und verwendet diese, um den Textknoten und den Offset an der Cursorposition zu erhalten. Der Wert der Variablen useShadows wird verwendet, um zu bestimmen, ob der im Text gehostete Shadow-Root an caretPositionFromPoint() übergeben wird.

  • Wenn der Browser diese Methode nicht unterstützt, prüft der Code dann auf document.caretRangeFromPoint und verwendet stattdessen diese.
  • Wenn der Knoten an der Cursorposition ein Textknoten ist, teilt der Code den Knoten an dem gewählten Offset und fügt einen Zeilenumbruch dazwischen.
  • Wenn der Knoten ein Elementknoten ist, fügt der Code an dem Offset einen Elementknoten für den Zeilenumbruch ein.
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
  }
}

Schließlich fügen wir zwei Klick-Ereignishandler für Absatzelemente im DOM und im Shadow-Root hinzu. Beachten Sie, dass wir die Elemente innerhalb des shadowRoot speziell abfragen müssen, da sie nicht für normale DOM-Abfragemethoden sichtbar sind.

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

// 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, false);
}

Ergebnisse

Klicken Sie im Lorem ipsum ... Absatz vor oder nach dem Shadow-DOM-Text, um an der Stelle, an der Sie klicken, einen Zeilenumbruch einzufügen. Beachten Sie, dass in diesem Fall das Protokoll zeigt, dass Sie einen TEXT_NODE ausgewählt haben, den Offset und einen Ausschnitt des ausgewählten Knotens mit einem ^ Zeichen bei dem Offset.

Zunächst wird der Shadow-Root nicht an caretPositionFromPoint() übergeben. Wenn Sie auf den Text "I'm in the shadow DOM" klicken, ist der zurückgegebene Cursorpositionsknoten der übergeordnete Knoten des Hosts am Offset des Shadow-Roots. Der Zeilenumbruch wird daher vor dem Knoten statt an der ausgewählten Stelle hinzugefügt. Beachten Sie, dass der Cursorpositionsknoten in diesem Fall den Typ ELEMENT_NODE hat.

Wenn Sie die Schaltfläche "Add shadow" klicken, wird der Shadow-Root an caretPositionFromPoint() übergeben, so dass die zurückgegebene Cursorposition der spezifische ausgewählte Knoten innerhalb des Shadow-DOM ist. Dadurch verhält sich der Shadow-DOM-Text wie der andere Absatztext.

Spezifikationen

Specification
CSSOM View Module
# dom-document-caretpositionfrompoint

Browser-Kompatibilität

BCD tables only load in the browser

Siehe auch