W3docs

JavaScript-Koordinaten

JavaScript wird verwendet, um Websites interaktiv zu machen. Es kann Objekte auf dem Bildschirm bewegen oder auf Benutzeraktionen reagieren. Koordinatensysteme verstehen.

Koordinaten sind numerische Werte, die eine Position auf der Seite beschreiben. Fast jede interaktive Funktion — Ziehen, Zeichnen, Tooltips, Popup-Menüs, Trefferkennung — läuft letztlich darauf hinaus, eine Koordinate zu lesen und etwas damit zu vergleichen oder zu positionieren.

Das Problem dabei ist, dass der Browser zwei verschiedene Koordinatensysteme bereitstellt, und deren Verwechslung ist die häufigste Ursache für den Fehler „mein Element springt an die falsche Stelle". Dieses Kapitel erklärt beide Systeme, wie man zwischen ihnen konvertiert, und die wichtigsten DOM-Methoden (getBoundingClientRect() und elementFromPoint()), mit denen Elementpositionen ausgelesen werden.

Die zwei Koordinatensysteme

Es gibt zwei Bezugssysteme, die man auseinanderhalten muss:

  • viewport-relative (Fenster-)Koordinaten — gemessen von der oberen linken Ecke des sichtbaren Teils der Seite. Diese ändern sich beim Scrollen nicht. Ein Punkt an der oberen linken Ecke des sichtbaren Bereichs hat immer die Koordinaten (0, 0). Eigenschaften: clientX / clientY.
  • Dokument-(Seiten-)Koordinaten — gemessen von der oberen linken Ecke des gesamten Dokuments, einschließlich des herausgescrollten Bereichs. Diese nehmen beim Scrollen nach unten zu. Eigenschaften: pageX / pageY.

Der Zusammenhang zwischen ihnen ergibt sich einfach aus dem Scroll-Offset:

pageX = clientX + window.scrollX;
pageY = clientY + window.scrollY;

Wenn die Seite nicht gescrollt ist, sind window.scrollX und window.scrollY beide 0, sodass beide Systeme identische Werte liefern — genau deshalb bleiben Scroll-Bugs unentdeckt, bis jemand die Seite scrollt.

viewport verwenden (clientX/Y) wenn…Dokument verwenden (pageX/Y) wenn…
Positionierung eines position: fixed-ElementsPositionierung eines position: absolute-Elements
Treffertest, was aktuell auf dem Bildschirm istSpeichern einer Klickposition zum späteren Wiederherstellen
Arbeiten mit getBoundingClientRect()Zeichnen auf einem langen, scrollbaren Canvas

Siehe JavaScript Window Sizes and Scrolling für die Messung von window.scrollX/Y und der viewport-Größe, und JavaScript Scrolling für das programmgesteuerte Verschieben der Scroll-Position.

Koordinaten aus einem Mausereignis lesen

Jedes Mausereignis enthält beide Koordinatensysteme. Das vollständige Ereignismodell ist unter Mouse Events Basics beschrieben.

viewport-Koordinaten: clientX / clientY

event.clientX und event.clientY liefern die Zeigerposition relativ zum viewport, unabhängig vom Scroll. Das folgende Beispiel befindet sich auf einer langen Seite, sodass man scrollen und bestätigen kann, dass die Zahlen im sichtbaren Bereich verankert bleiben:

<!-- snippet: html-result -->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Client-Side Coordinates Example</title>
    <style>
#container {
  width: 100%;
  height: 100%;
  background-color: grey;
  min-height: 40px;
}
    </style>
</head>
<body style="height: 2000px;">
    <div id="container">Click anywhere in the grey area to see Client-Side coordinates!</div>
    <script>
        const container = document.getElementById('container');
        function showCoords(event) {
            alert("Client-Side X: " + event.clientX + ", Y: " + event.clientY);
        }
        container.addEventListener('click', showCoords);
    </script>
</body>
</html>

Dokument-Koordinaten: pageX / pageY

event.pageX und event.pageY liefern die Zeigerposition relativ zum gesamten Dokument und beinhalten daher bereits den Scroll-Offset. Sie sind standardisiert und weit verbreitet — es ist nicht nötig, sie manuell zu berechnen. Das folgende Beispiel zeigt sie neben der manuellen Berechnung clientX + window.scrollX, damit man überprüfen kann, dass beide übereinstimmen:

<!-- snippet: html-result -->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Page-Side Coordinates Example</title>
    <style>
#container {
  width: 100%;
  height: 100%;
  background-color: grey;
  min-height: 40px;
}
    </style>
</head>
<body style="height: 2000px;">
    <div id="container">Click anywhere in this area to see Page-Side coordinates!</div>
    <script>
        const container = document.getElementById('container');
        function showPageCoords(event) {
            // pageX/pageY already include scroll; the manual version must match
            const manualX = event.clientX + window.scrollX;
            const manualY = event.clientY + window.scrollY;
            alert("pageX/Y: " + event.pageX + ", " + event.pageY +
                  "\nclientX+scrollX: " + manualX + ", " + manualY);
        }
        container.addEventListener('click', showPageCoords);
    </script>
</body>
</html>

Bildschirmkoordinaten: screenX / screenY

event.screenX und event.screenY messen die Zeigerposition relativ zum gesamten physischen Bildschirm, nicht nur zum Browserfenster. Sie sind nützlich, um den Cursor über mehrere Monitore hinweg zu verfolgen oder ein window.open()-Popup zu positionieren. Für Arbeiten innerhalb der Seite sind jedoch fast immer clientX/Y oder pageX/Y die bessere Wahl.

Die Position eines Elements lesen: getBoundingClientRect()

Um herauszufinden, wo sich ein Element befindet (anstatt wo sich die Maus befindet), ruft man getBoundingClientRect() auf. Diese Methode gibt ein DOMRect zurück, dessen Werte top, right, bottom, left, width und height alle in viewport-Koordinaten angegeben sind — demselben System wie clientX/Y.

const rect = element.getBoundingClientRect();
// rect.left, rect.top   → top-left corner, relative to the viewport
// rect.width, rect.height → rendered size including borders/padding

Da das Rect viewport-relativ ist, ändert es sich beim Scrollen. Um die Position des Elements in Dokument-Koordinaten zu erhalten, addiert man den Scroll-Offset:

function getDocumentCoords(el) {
  const rect = el.getBoundingClientRect();
  return {
    top: rect.top + window.scrollY,
    left: rect.left + window.scrollX,
  };
}

Diese Kombination — mit getBoundingClientRect() lesen und scrollX/Y addieren, wenn Seitenkoordinaten benötigt werden — ist die Konvertierung, die man ständig verwenden wird.

Das Element an einer Position finden: elementFromPoint()

Die umgekehrte Frage — „Was befindet sich unter diesen Koordinaten?" — beantwortet document.elementFromPoint(x, y). Diese Methode nimmt viewport-Koordinaten (passend zu clientX/Y) und gibt das oberste Element an dieser Stelle zurück, oder null, wenn der Punkt außerhalb des viewport liegt.

document.addEventListener('click', (event) => {
  const el = document.elementFromPoint(event.clientX, event.clientY);
  console.log('You clicked on:', el.tagName);
});

Ein häufiger Fallstrick: Übergibt man pageX/pageY auf einer gescrollten Seite, gibt die Methode das falsche Element (oder null) zurück, weil sie viewport-Koordinaten erwartet, keine Dokumentkoordinaten.

Elementpositionen mit Koordinaten manipulieren

Diese Bausteine lassen sich kombinieren, um Dinge zu bewegen. Die Methode getBoundingClientRect() ermöglicht es, den Versatz zwischen dem Zeiger und der Elementecke zu berechnen, sodass das Element beim Starten des Ziehens nicht zum Cursor „springt". Hier ist ein Quadrat, das man herumschieben kann:

Beispiel: Ziehbares HTML-Element

<!-- snippet: html-result -->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Draggable Element Example</title>
    <style>
        #draggable {
            width: 100px;
            height: 100px;
            background-color: red;
            position: absolute;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="draggable"></div>
    <script>
        const elem = document.getElementById('draggable');
        let shiftX, shiftY;

        function onMouseDown(event) {
            shiftX = event.clientX - elem.getBoundingClientRect().left;
            shiftY = event.clientY - elem.getBoundingClientRect().top;

            function moveAt(pageX, pageY) {
                elem.style.left = pageX - shiftX + 'px';
                elem.style.top = pageY - shiftY + 'px';
            }

            function onMouseMove(event) {
                moveAt(event.clientX + window.scrollX, event.clientY + window.scrollY);
            }

            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', function stopDrag() {
                document.removeEventListener('mousemove', onMouseMove);
                document.removeEventListener('mouseup', stopDrag);
                elem.removeEventListener('mousedown', onMouseDown);
            });
        }

        elem.addEventListener('mousedown', onMouseDown);
        elem.addEventListener('dragstart', function() { return false; });
    </script>
</body>
</html>
Hinweis

Für bessere Performance sollte man transform: translate() anstelle von left / top für Animationen in Betracht ziehen, da dies Layout-Neuberechnungen vermeidet. Für die Mobilgeräte-Kompatibilität sollte man außerdem touchstart-, touchmove- und touchend-Event-Listener neben den Mausereignissen hinzufügen.

Mit Koordinaten animieren

Bewegungen lassen sich auch steuern, indem man die Koordinaten eines Elements bei jedem Frame mit requestAnimationFrame aktualisiert. Die Multiplikation mit einem Zeitdelta sorgt dafür, dass die Geschwindigkeit unabhängig von der Bildwiederholrate des Displays konstant bleibt:

Beispiel: Animiertes, sich bewegendes Objekt

<!-- snippet: html-result -->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Animation Using Coordinates</title>
</head>
<body>
    <div id="animateMe" style="width: 50px; height: 50px; background: blue; position: absolute;"></div>
    <button id="stopBtn">Stop Animation</button>
    <script>
        const target = document.getElementById('animateMe');
        const stopBtn = document.getElementById('stopBtn');
        let pos = 0;
        let isRunning = true;
        let lastTime = performance.now();

        function animate(currentTime) {
            if (!isRunning) return;
            const delta = (currentTime - lastTime) / 16; // Normalize to ~60fps
            lastTime = currentTime;
            if (pos >= 350) {
                pos = 0;
            }
            pos += delta;
            target.style.left = pos + 'px';
            requestAnimationFrame(animate);
        }

        stopBtn.addEventListener('click', () => { isRunning = false; });
        requestAnimationFrame(animate);
    </script>
</body>
</html>
Info

Obwohl JavaScript leistungsstarke Möglichkeiten zur Erstellung dynamischer und interaktiver Animationen bietet, sind CSS-Animationen für einfachere Animationen oft besser geeignet. CSS-Animationen können flüssigere Übergänge bieten und sind in der Regel performanter, da sie direkt von der Rendering-Engine des Browsers verarbeitet werden und weniger CPU-Ressourcen verbrauchen. Dadurch sind CSS-Animationen ideal für Effekte wie Übergänge, Einblendungen und einfache Bewegungen, insbesondere wenn hohe Performance und geringer Ressourcenverbrauch entscheidend sind.

Fazit

Die wichtigste Erkenntnis ist, dass der Browser zwei Koordinatensysteme hat: viewport (clientX/Y, getBoundingClientRect(), elementFromPoint()) und Dokument (pageX/Y). Die Konvertierung zwischen ihnen erfolgt durch Addition oder Subtraktion von window.scrollX/Y. Hält man diese auseinander, verschwinden die meisten Positionierungsfehler.

Als nächsten Schritt empfiehlt sich ein Blick in Window Sizes and Scrolling, Scrolling zum Verschieben des viewport und Mouse Events Basics für die Ereignisse, die diese Koordinaten liefern.

Übungen

Übung
Was repräsentieren clientseitige Koordinaten in JavaScript?
Was repräsentieren clientseitige Koordinaten in JavaScript?
Was this page helpful?