jQuery / Methode / .offset() - Dokumentkoordinaten lesen oder setzen
jQuerys .offset() liefert { top, left } der Border-Box des ersten ausgewählten Elements relativ zur linken oberen Dokumentecke. .offset({ top, left }) verschiebt ausgewählte Elemente auf diese Dokumentkoordinaten. Verwechseln Sie sie nicht mit Viewport- oder Elternkoordinaten.
offset, position und Viewport unterscheiden
Die .offset()-API misst die Border-Box ohne margin relativ zum Dokument. top/left sind Zahlen in CSS-Pixeln und können Nachkommastellen haben. Lesen allein verschiebt nichts.
| Methode | Bezugsrahmen | Zielgrenze |
|---|---|---|
| .offset() | Dokument | Border-Box |
| .position() | Padding-Box des Offset-Elternteils | Margin-Box des Ziels |
| getBoundingClientRect() | Viewport | Rechteck einschließlich Rahmen |
Der Offset-Elternteil von .position() ist nicht zwangsläufig der direkte HTML-Elternteil. top/left von getBoundingClientRect() ändern sich beim Scrollen; window.scrollY/scrollX ergeben durch Addition normale Dokumentkoordinaten. Übertragen Sie unterschiedliche Bezugsrahmen nicht unverändert in CSS top/left.
Drei Bezugsrahmen und Scrollen vergleichen
Speichern Sie alles in UTF-8 als offset-demo.html und öffnen Sie es im Browser. Für die offizielle jQuery-Datei 4.0.0 vom Content Delivery Network (CDN) brauchen Sie Internet, aber keine Installation. Lesen, Scrollen, Verschieben, Verbergen, Reset und der Funktionssetter unten wurden mit 3.7.1 und 4.0.0 ausgeführt.
stage steht bei Dokument-top 500/left 20 mit Rahmen 5. target verwendet darin top 40/left 50 mit margin 10. Body-margin ist null, Transformationen fehlen, daher lässt sich direkt rechnen. Bedienelemente bleiben oben im Viewport fest.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document and viewport coordinates</title>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<style>
body { margin: 0; min-height: 1800px; font: 16px/1.6 sans-serif; }
#controls { position: fixed; inset: 0 0 auto; padding: 12px; background: #fff; z-index: 10; max-height: 42vh; overflow: auto; }
button { font: inherit; margin: 3px; }
pre { white-space: pre-wrap; overflow-wrap: anywhere; }
#stage { position: absolute; top: 500px; left: 20px; width: 300px; height: 260px; border: 5px solid #bbc; }
#target { position: absolute; top: 40px; left: 50px; margin: 10px; border: 2px solid teal; padding: 8px; background: #d9f4ef; }
</style>
</head>
<body>
<div id="controls">
<strong>Measure the target border box</strong>
<button type="button" id="read">Read</button>
<button type="button" id="scroll">Scroll 200px</button>
<button type="button" id="move">Move to (top 620, left 130)</button>
<button type="button" id="visibility">Toggle visibility</button>
<button type="button" id="display">Toggle display</button>
<button type="button" id="reset">Reset</button>
<pre id="result" aria-live="polite"></pre>
</div>
<div id="stage"><div id="target">Target</div></div>
<script>
$(function () {
const $target = $("#target");
function n(value) { return Number(value.toFixed(2)); }
function report() {
const node = $target.get(0);
if (!node) { $("#result").text("Target missing"); return; }
if (!node.getClientRects().length) { $("#result").text("Not rendered; reveal before measuring"); return; }
const offset = $target.offset();
const rect = node.getBoundingClientRect();
const position = $target.position();
$("#result").text(`document top=${n(offset.top)}, left=${n(offset.left)}\nviewport top=${n(rect.top)}, left=${n(rect.left)}\nposition top=${n(position.top)}, left=${n(position.left)}\nscrollY=${n(window.scrollY)}`);
}
$("#read").on("click", report);
$("#scroll").on("click", function () { window.scrollTo(0, 200); });
$("#move").on("click", function () { if ($target.get(0).getClientRects().length) $target.offset({ top: 620, left: 130 }); report(); });
$("#visibility").on("click", function () { $target.css("visibility", $target.css("visibility") === "hidden" ? "visible" : "hidden"); report(); });
$("#display").on("click", function () { $target.toggle(); report(); });
$("#reset").on("click", function () { $target.css({ top: 40, left: 50, display: "", visibility: "" }); window.scrollTo(0, 0); report(); });
$(window).on("scroll.offsetLesson resize.offsetLesson", report);
report();
});
</script>
</body>
</html>
Die Anfangswerte gelten bei Scrollposition null. Nur einen Ausschnitt in eine andere Seite zu kopieren kann wegen anderer Umgebung abweichen.
document top=555, left=85
viewport top=555, left=85
position top=40, left=50
scrollY=0
- Read misst neu. Dokument-top ist 500 + 5 + 40 + 10 = 555, left 20 + 5 + 50 + 10 = 85.
- Scroll 200px behält Dokument-top 555, Viewport-top wird 355 und scrollY 200. .position() bleibt 40/50.
- Move to (top 620, left 130) setzt Dokument 620/130. Bei Scroll 200 ist Viewport 420/130 und .position() 105/95: Eltern-CSS-Werte unterscheiden sich.
- Toggle visibility verbirgt target, behält aber seine Layoutbox und Messbarkeit.
- Toggle display setzt display:none. Statt nicht unterstützter Zahlen erscheint Not rendered. Nochmals zeigt und misst wieder.
- Reset stellt Position und Sichtbarkeit wieder her und scrollt auf null.
Hier ist target normal absolut positioniert. Bei gleichbleibender Dokumentposition verändert bloßes Fensterscrollen die Dokumentkoordinaten nicht. Übertragen Sie das nicht pauschal auf fixed/sticky oder Layoutänderungen. Messen Sie bei Bedarf nach Bild-, Schrift- oder Größenänderungen erneut.
Koordinaten setzen und Funktion verwenden
.offset({ top: 620, left: 130 }) nimmt Dokumentkoordinaten an, keine elternrelativen CSS-Werte. jQuery berechnet die notwendigen Positionierungswerte und kann static beim Setzen zu relative ändern. Prüfen Sie auch Auswirkungen auf CSS und Layout.
.offset(function(index, current)) erhält aktuelle Dokumentkoordinaten je Element und liefert neue top/left. Führen Sie diesen vollständigen Teilcode in der Entwicklerkonsole aus. Zwei temporäre Elemente erhalten unterschiedliche vertikale Ergänzungen und werden zuletzt entfernt.
const $items = $("<div>A</div><div>B</div>")
.css({ position: "absolute", top: 1000, left: 20 }).appendTo("body");
$items.offset(function (index, current) {
return { top: current.top + index * 30, left: current.left + 40 };
});
console.log($items.map(function () {
const point = $(this).offset();
return `${point.top},${point.left}`;
}).get().join(" | "));
console.log(`empty getter=${typeof $().offset()}`);
$items.remove();
1000,60 | 1030,60
empty getter=undefined
Der Getter liest das erste Element, Setter bearbeiten alle ausgewählten. index ist nullbasiert in der Menge. Die Beispielrundung betrifft nur die Anzeige; verwerfen Sie Nachkommastellen nicht automatisch in Berechnungen.
Verborgene Elemente, leere Auswahl und Änderungen
display:none hat keine Renderingbox und ist keine unterstützte Quelle sinnvoller aktueller Positionen. Auch zurückgegebene Nullen sind nicht als echte Lage zu verwenden: erst anzeigen, dann messen. visibility:hidden behält die Box und ist messbar.
Das Beispiel prüft Knotenexistenz und getClientRects().length. Bei leerer jQuery-Auswahl ist .offset() undefined; .offset().top direkt zu lesen schlägt fehl. Auch ein vom Document Object Model (DOM) getrennter Knoten liefert nicht automatisch die beabsichtigte Dokumentposition.
Prüfen Sie Zoom, transform, geänderte margins/borders und Vorfahrenlayout. Die API unterstützt keine Berücksichtigung von margins am html-Element. Gleichen Sie zuerst die Bezugsrahmen ab und aktualisieren Sie bei relevanten Layoutänderungen statt unnötiger Mess- und Schreibwiederholungen.




