Start · Sprachen · JavaScript · Referenz · ShadowRoot.elementFromPoint()

ShadowRoot.elementFromPoint()

Methode

Gibt das oberste Element innerhalb eines Shadow-Roots an den angegebenen Viewport-Koordinaten zurück, oder <code>null</code>, wenn dort keines liegt.

seit JavaScript Baseline: alle modernen Browser (Chrome 53+, Firef Kategorie: webapi

Signatur

ShadowRoot.prototype.elementFromPoint(x, y)

Beschreibung

elementFromPoint() auf einem ShadowRoot funktioniert wie document.elementFromPoint(). Der Unterschied: Das Ergebnis wird aus Sicht dieses Shadow-Baums bestimmt. Die Methode sucht das oberste Element, das an den Koordinaten (x, y) relativ zum Viewport gerendert wird. Liegt dieses Element im Shadow-DOM, bekommst du das echte innere Element und nicht den Host.

Hintergrund: document.elementFromPoint() wendet Retargeting an. Es liefert für Elemente in einem Shadow-Baum nur das Host-Element, damit die Kapselung erhalten bleibt. Wenn du innerhalb einer Web Component wissen willst, welches interne Element unter dem Mauszeiger oder unter einem Touch-Punkt liegt, rufst du die Methode direkt auf dem eigenen shadowRoot auf.

Typische Einsätze sind eigene Drag-and-Drop-Logik in Komponenten, Trefferprüfung (Hit-Testing) bei Touch-Gesten ohne Ereignisziel, Tooltips und Barrierefreiheits- oder Test-Werkzeuge. Die Methode stammt aus dem Mixin DocumentOrShadowRoot. Für alle übereinanderliegenden Elemente an einem Punkt gibt es elementsFromPoint().

Parameter

Name Typ Default Beschreibung
$x Pflicht number Horizontale Koordinate in CSS-Pixeln relativ zur linken Kante des Viewports, also nicht relativ zum Dokument oder zum Host-Element.
$y Pflicht number Vertikale Koordinate in CSS-Pixeln relativ zur oberen Kante des Viewports.

Rückgabewert

Typ
Element | null
Beschreibung
Das oberste Element an diesem Punkt, gesehen aus diesem Shadow-Root. Liegt das Element außerhalb dieses Shadow-Baums, wird es wie beim Retargeting auf ein Element in diesem Baum oder den umgebenden Host abgebildet. null, wenn der Punkt außerhalb des Viewports liegt oder dort nichts gerendert wird.

Beispiele

Inneres Element unter dem Mauszeiger ermitteln

class HoverInfo extends HTMLElement {
  constructor() {
    super();
    const root = this.attachShadow({ mode: 'open' });
    root.innerHTML = `
      <style>button { margin: 4px; }</style>
      <button id="a">Speichern</button>
      <button id="b">Abbrechen</button>`;
  }
  connectedCallback() {
    document.addEventListener('pointermove', (e) => {
      const inner = this.shadowRoot.elementFromPoint(e.clientX, e.clientY);
      const outer = document.elementFromPoint(e.clientX, e.clientY);
      if (inner?.tagName === 'BUTTON') {
        console.log('Shadow:', inner.id, '| Dokument:', outer.tagName);
      }
    });
  }
}
customElements.define('hover-info', HoverInfo);
document.body.append(document.createElement('hover-info'));
Shadow: a | Dokument: HOVER-INFO

Drop-Ziel beim Ziehen per Touch bestimmen

const host = document.createElement('div');
document.body.append(host);
const root = host.attachShadow({ mode: 'open' });
root.innerHTML = `
  <style>.zone { width: 150px; height: 80px; border: 2px dashed; margin: 8px; }</style>
  <div class="zone" data-name="Eingang"></div>
  <div class="zone" data-name="Archiv"></div>`;

// Bei touchmove ist e.target immer das Startelement,
// daher wird das Ziel über die Koordinaten ermittelt
document.addEventListener('touchend', (e) => {
  const { clientX, clientY } = e.changedTouches[0];
  const target = root.elementFromPoint(clientX, clientY);
  const zone = target?.closest('.zone');
  console.log(zone ? `Abgelegt in: ${zone.dataset.name}` : 'Kein Drop-Ziel');
});
Abgelegt in: Archiv

Trefferprüfung an der Mitte eines Elements

const host = document.createElement('div');
document.body.append(host);
const root = host.attachShadow({ mode: 'open' });
root.innerHTML = '<p id="text">Hallo</p><div id="overlay"></div>';

const isVisibleAtCenter = (el) => {
  const r = el.getBoundingClientRect();
  const hit = el.getRootNode().elementFromPoint(r.left + r.width / 2, r.top + r.height / 2);
  return hit === el || el.contains(hit);
};

console.log(isVisibleAtCenter(root.getElementById('text')));
true

// Wichtig · Fallstricke

  • Koordinaten beziehen sich auf den Viewport: Verwende clientX/clientY und nicht pageX/pageY. Punkte außerhalb des sichtbaren Bereichs liefern null.
  • Nicht standardisiert: Die Methode steht nicht in der DOM-Spezifikation, sondern gilt als Erweiterung aus CSSOM View bzw. DocumentOrShadowRoot. Alle modernen Browser unterstützen sie, im Detail kann sich das Verhalten aber unterscheiden, etwa bei verschachtelten Shadow-Roots oder bei Elementen außerhalb des Baums.
  • Geschlossene Shadow-Roots: Bei mode: 'closed' ist host.shadowRoot gleich null. Du brauchst dann die Referenz, die attachShadow() zurückgegeben hat.
  • Elemente, die Treffer ignorieren: Elemente mit pointer-events: none oder visibility: hidden werden übersprungen. Das Ergebnis kann dann ein darunterliegendes Element sein.
  • Performance: Jeder Aufruf kann ein Layout erzwingen. Bei häufigen Ereignissen wie pointermove die Aufrufe drosseln oder mit requestAnimationFrame bündeln.
  • Verschachtelte Komponenten: Liegt der Punkt über dem Host einer weiteren, verschachtelten Komponente, bekommst du nur diesen Host und nicht deren Innenleben. Für das Innenleben rufst du die Methode auf dem jeweiligen shadowRoot erneut auf.