Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten. Erfahre mehr über dieses Experiment.

View in English Always switch to English

Dokument: 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 den DOM-Knoten zusammen mit dem Cursor und dem Zeichenoffset des Cursors innerhalb dieses Knotens enthält.

Syntax

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

Parameter

x

Die horizontale Koordinate eines Punkts.

y

Die vertikale Koordinate eines Punkts.

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 einen Knoten zurückgeben, der innerhalb des Shadow DOMs einer bereitgestellten Shadow-Root definiert ist. Wenn die Cursorposition in eine nicht bereitgestellte Shadow-Root fällt, wird die zurückgegebene CaretPosition auf den Knoten abgebildet, der der Host der Shadow-Root ist.

Rückgabewert

Ein CaretPosition-Objekt oder null.

Der zurückgegebene Wert ist null, wenn 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 anzeigen, an dem kein Textinsertion-Punkt-Indikator eingefügt werden konnte.

Beispiele

Textknoten an der Cursorposition im DOM aufteilen

Dieses Beispiel demonstriert, 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, sofern unterstützt, mit der nicht standardmäßigen Document.caretRangeFromPoint()-Methode als Fallback.

Beachten Sie, dass einige Teile des Codes ausgeblendet sind, einschließlich des Codes, der zum Protokollieren verwendet wird, da dies nicht zum Verständnis dieser Methode beiträgt.

HTML

Das HTML definiert einen Textabsatz.

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 unten überprüft zuerst die Unterstützung von document.caretPositionFromPoint und verwendet sie, um den Textknoten und den Offset an der Cursorposition zu erhalten. Wenn der Browser diese Methode nicht unterstützt, prüft der Code als nächstes document.caretRangeFromPoint und verwendet diese stattdessen.

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

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 Klickereignis-Handler für beliebige Absatzelemente hinzugefügt.

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

Ergebnisse

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

Textknoten an Cursorpositionen in einem Shadow DOM aufteilen

Dieses Beispiel zeigt, wie man die Cursorposition von einem ausgewählten Knoten innerhalb einer Shadow-Root erhält. Das Beispiel ist dem ausschließlich DOM-basierten Beispiel oben sehr ähnlich, außer dass ein Teil des Textes innerhalb einer Shadow-Root liegt. Wir stellen einen Button zur Verfügung, um Ihnen den Unterschied zu zeigen, wenn eine Shadow-Root zu caretPositionFromPoint() übergeben wird oder nicht.

Beachten Sie, dass einige Teile des Codes ausgeblendet sind, einschließlich des Codes, der zum Protokollieren verwendet wird, da dies nicht zum Verständnis dieser Methode beiträgt.

HTML

Das HTML definiert einen Textabsatz innerhalb eines <div>-Elements. Der Absatz enthält ein <span>-Element mit der id "host", das wir als Host für eine Shadow-Root verwenden werden. Es gibt auch einige Buttons, die wir verwenden werden, um das Beispiel zurückzusetzen und das Shadow-Root-Option-Argument zu caretPositionFromPoint() hinzuzufügen/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 das Unterscheiden 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 eine Shadow-Root dynamisch anzuhängen, da das MDN-Beispielsystem uns dies nicht deklarativ mit dem <template>-Element tun lässt. 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 unseren "Enable/Disable shadow"-Button hinzu. Dieser Code schaltet den Wert der useShadows-Variable um und aktualisiert den Button-Text entsprechend.

js
let useShadows = false;

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

Die Methode unten überprüft zuerst die Unterstützung von document.caretPositionFromPoint und verwendet sie, um den Textknoten und den Offset an der Cursorposition zu erhalten. Der Wert der useShadows-Variable wird verwendet, um zu bestimmen, ob die im Text gehostete Shadow-Root an caretPositionFromPoint() übergeben wird.

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

Zum Schluss fügen wir zwei Klickereignis-Handler für Absatzelemente im DOM und im Shadow-Root hinzu. Beachten Sie, dass wir die Elemente innerhalb des shadowRoot spezifisch abfragen müssen, da sie für normale DOM-Abfragemethoden nicht 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);
}

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

Ergebnisse

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

Anfangs wird die Shadow-Root nicht an caretPositionFromPoint() übergeben, daher ist der zurückgegebene Cursorposition-Knoten beim Klicken auf den Text "I'm in the shadow DOM" der übergeordnete Knoten des Hosts, mit dem Offset der Shadow-Root. Der Zeilenumbruch wird daher vor dem Knoten hinzugefügt und nicht an dem ausgewählten Punkt. Beachten Sie, dass der Cursorposition-Knoten in diesem Fall den Typ ELEMENT_NODE hat.

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

Spezifikationen

Specification
CSSOM View Module
# dom-document-caretpositionfrompoint

Browser-Kompatibilität

Siehe auch