jQuery / Méthode / .offset() - Lire ou définir les coordonnées du document

Avec jQuery, .offset() renvoie { top, left } pour la boîte de bordure du premier élément sélectionné, par rapport au coin supérieur gauche du document. .offset({ top, left }) déplace les éléments sélectionnés vers ces coordonnées. Ne les confondez pas avec celles de la zone visible ou du parent.

Distinguer offset, position et zone visible

L’API .offset() mesure la boîte de bordure sans la marge de l’élément. top et left sont des nombres en pixels CSS, éventuellement fractionnaires. Leur lecture ne déplace pas l’élément.

Méthode Référence Limite mesurée
.offset() Document Boîte de bordure
.position() Boîte de remplissage du parent de positionnement Boîte de marge de l’élément
getBoundingClientRect() Zone visible du navigateur Rectangle comprenant les bordures

Le parent de positionnement de .position() n’est pas nécessairement le parent HTML direct. Les valeurs top/left de getBoundingClientRect() changent lors du défilement. Ajouter window.scrollY/scrollX les convertit en coordonnées ordinaires du document. Ne copiez pas directement des valeurs d’un autre repère dans CSS top/left.

Comparer les trois repères avec un exemple complet

Enregistrez tout le code en UTF-8 dans offset-demo.html et ouvrez le fichier dans le navigateur. Internet est nécessaire pour charger le fichier jQuery officiel 4.0.0 depuis le réseau de diffusion de contenu (CDN), sans installation. La lecture, le défilement, le déplacement, le masquage, la réinitialisation et la fonction de définition ci-dessous ont été exécutés avec 3.7.1 et 4.0.0.

stage est placé à top 500/left 20 dans le document, avec une bordure de 5 pixels. target utilise top 40/left 50 et une marge de 10 à l’intérieur. La marge de body est nulle et aucune transformation n’intervient : les valeurs sont calculables directement. Les commandes restent fixées en haut de la zone visible.

<!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>

Voici le résultat initial sans défilement. Copier seulement un fragment dans une autre page peut modifier ces valeurs à cause de la disposition environnante.

document top=555, left=85
viewport top=555, left=85
position top=40, left=50
scrollY=0
  1. Read mesure à nouveau. top vaut 500 + 5 + 40 + 10 = 555 et left vaut 20 + 5 + 50 + 10 = 85 dans le document.
  2. Scroll 200px conserve top 555 dans le document, mais donne top 355 dans la zone visible et scrollY 200. .position() reste à 40/50.
  3. Move to (top 620, left 130) définit les coordonnées du document à 620/130. Après un défilement de 200, la zone visible donne 420/130 et .position() 105/95 : les valeurs CSS relatives au parent diffèrent.
  4. Toggle visibility masque target en conservant sa boîte de mise en page, qui reste mesurable.
  5. Toggle display applique display:none. L’exemple affiche Not rendered plutôt que des mesures non prises en charge. Appuyez de nouveau pour afficher et mesurer.
  6. Reset rétablit la position CSS, la visibilité et le défilement à zéro.

La cible est ici un élément ordinaire en position absolue. Si sa position dans le document reste identique, le seul défilement de la fenêtre ne change pas ses coordonnées du document. Ne généralisez pas cette règle aux positions fixed/sticky ou aux changements de mise en page. Mesurez de nouveau si nécessaire après le chargement des images ou des polices, ou après un changement de dimensions.

Définir les coordonnées et employer une fonction

.offset({ top: 620, left: 130 }) reçoit des coordonnées du document et non des valeurs CSS relatives au parent. jQuery calcule les valeurs de positionnement nécessaires et peut remplacer static par relative lors d’une définition. Vérifiez également le CSS et la disposition obtenus.

.offset(function(index, current)) reçoit les coordonnées actuelles de chaque élément et renvoie les nouveaux top/left. Exécutez ce fragment complet dans la console des outils de développement. Il crée deux éléments temporaires, applique une augmentation verticale différente à chacun, puis les supprime.

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

La lecture concerne le premier élément ; la définition concerne tous les éléments sélectionnés. index commence à zéro dans la collection. L’exemple arrondit uniquement l’affichage pour faciliter la lecture, pas les coordonnées des calculs. Ne supprimez pas systématiquement les décimales.

Vérifier les éléments masqués et les changements de disposition

display:none supprime la boîte de rendu : ce n’est pas une source prise en charge de coordonnées actuelles significatives. Même si une version renvoie zéro, ne le considérez pas comme une position réelle : affichez d’abord l’élément. visibility:hidden conserve sa boîte et permet la mesure.

L’exemple vérifie l’existence du nœud et getClientRects().length. Une sélection jQuery vide renvoie undefined avec .offset() ; lire directement .offset().top échouerait. Un nœud détaché du modèle objet du document (DOM) ne fournit pas non plus automatiquement la position attendue dans le document.

Contrôlez le zoom, les transformations, les marges et bordures modifiées et la disposition des ancêtres. L’API ne prend pas en charge les marges de l’élément html. Alignez d’abord les repères, puis actualisez lors des changements pertinents plutôt que de répéter inutilement lectures géométriques et écritures CSS.