Recherche de la position de l'élément par rapport au document

113

Quelle est la manière la plus simple de déterminer la position d'un élément par rapport à la fenêtre du document / corps / navigateur?

Pour le moment, j'utilise .offsetLeft/offsetTop, mais cette méthode ne vous donne que la position par rapport à l'élément parent, vous devez donc déterminer le nombre de parents de l'élément body, pour connaître la position relative au corps / fenêtre du navigateur / position du document.

Cette méthode est également trop lourde.

Einstein
la source

Réponses:

58

Vous pouvez parcourir le offsetParentniveau supérieur du DOM.

function getOffsetLeft( elem )
{
    var offsetLeft = 0;
    do {
      if ( !isNaN( elem.offsetLeft ) )
      {
          offsetLeft += elem.offsetLeft;
      }
    } while( elem = elem.offsetParent );
    return offsetLeft;
}
KeatsKelleher
la source
28
Non, ce n'est pas le cas, quand un parent (en particulier un élément html !!!) a des marges, des rembourrages ou des bordures.
Flash Thunder
en ce qui concerne la marge ... il pourrait être utile de définir le dimensionnement de la boîte sur border-box ... ou quelque chose du genre ... rien qui ne puisse être corrigé ...
Tout d'abord, vous devez décider de prendre en compte la bordure et la marge en fonction du dimensionnement de la boîte. Deuxièmement, la marge réduite doit être prise en compte. Et enfin, il y aura une situation plus compliquée à l'avenir qui aggravera cette réponse.
xianshenglu
6
Pourquoi est-ce la réponse acceptée? C'est obsolète et peu performant. Rendez-vous service et utilisez getBoundingClientRect () comme suggéré dans les autres réponses.
Fabian von Ellerts
177

Vous pouvez aller en haut et à gauche sans parcourir le DOM comme ceci:

function getCoords(elem) { // crossbrowser version
    var box = elem.getBoundingClientRect();

    var body = document.body;
    var docEl = document.documentElement;

    var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
    var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;

    var clientTop = docEl.clientTop || body.clientTop || 0;
    var clientLeft = docEl.clientLeft || body.clientLeft || 0;

    var top  = box.top +  scrollTop - clientTop;
    var left = box.left + scrollLeft - clientLeft;

    return { top: Math.round(top), left: Math.round(left) };
}
basilic
la source
6
Cette réponse ne tient pas compte des compensations des parents. C'est seulement relatif à la fenêtre d'
affichage
3
@Nickey, ce n'est pas vrai: la position de la fenêtre, plus les vérifications de décalage de défilement, donnent les coordonnées relatives à l'ensemble du document.
natchiketa
1
Peut être bogué sur les appareils mobiles stackoverflow.com/a/32623832/962634 Besoin de recherche
Basil
1
Pourquoi soustrayez-vous clientTop / clientLeft?
Teemoh
1
@Teemoh clientTop / clientLeft correspond aux valeurs de bordure sur l' <html>élément.
Raphael Rafatpanah
98

Vous pouvez utiliser element.getBoundingClientRect() pour récupérer la position de l'élément par rapport à la fenêtre.

Ensuite, utilisez document.documentElement.scrollTop pour calculer le décalage de la fenêtre.

La somme des deux donnera la position de l'élément par rapport au document:

element.getBoundingClientRect().top + document.documentElement.scrollTop
dy_
la source
10
Relative à la fenêtre n'est pas la même chose que relative au document. Si la page défile un peu vers le bas, le haut par rapport à la fenêtre sera un nombre plus petit que le haut par rapport au document.
MrVimes
14
il commence par rapport à la fenêtre et ajoute scrollY pour l'obtenir par rapport au document.
Joaquin Cuenca Abela
2
document.documentElement.scrollTopne fonctionne pas dans Safari, utilisez à la window.scrollYplace
Fabian von Ellerts
7

Je suggère d'utiliser

element.getBoundingClientRect()

comme proposé ici au lieu du calcul manuel de l'offset via offsetLeft , offsetTop et offsetParent . comme proposé ici Dans certaines circonstances * le parcours manuel produit des résultats invalides. Voir ce Plunker: http://plnkr.co/pC8Kgj

* Lorsque l'élément est à l'intérieur d'un parent scrollable avec un positionnement statique (= par défaut).

HANiS
la source
Vous pouvez également faire fonctionner le calcul de décalage en y incorporant scrollLeft et scrollTop.
Ben J
Mais j'ai trouvé offsetLeftet offsetTopje ne prends pas en compte les transformations CSS3, ce qui le getBoundingClientRect()fait. C'est donc un cas, les résultats peuvent être invalides. Si vous en avez besoin, vous pouvez obtenir le décalage d'un élément par rapport au parent (comme offsetLeft) en utilisant (element.getBoundingClientRect().left - parent.getBoundingClientRect().left).
Ben J
devrait def monter plus haut; ces valeurs sont bien plus utiles, et le tout en un seul appel aussi!
mix3d
7

document-offset( Script tiers ) est intéressant et semble tirer parti des approches des autres réponses ici.

Exemple:

var offset = require('document-offset')
var target = document.getElementById('target')
console.log(offset(target))
// => {top: 69, left: 108} 
Mathieu M-Gosselin
la source
5

J'ai trouvé que la méthode suivante était la plus fiable pour traiter les cas de bord qui déclenchent offsetTop / offsetLeft.

function getPosition(element) {
    var clientRect = element.getBoundingClientRect();
    return {left: clientRect.left + document.body.scrollLeft,
            top: clientRect.top + document.body.scrollTop};
}
Robin Stewart
la source
4

Pour ceux qui veulent obtenir les coordonnées x et y de différentes positions d'un élément, par rapport au document.

const getCoords = (element, position) => {
  const { top, left, width, height } = element.getBoundingClientRect();
  let point;
  switch (position) {
    case "top left":
      point = {
        x: left + window.pageXOffset,
        y: top + window.pageYOffset
      };
      break;
    case "top center":
      point = {
        x: left + width / 2 + window.pageXOffset,
        y: top + window.pageYOffset
      };
      break;
    case "top right":
      point = {
        x: left + width + window.pageXOffset,
        y: top + window.pageYOffset
      };
      break;
    case "center left":
      point = {
        x: left + window.pageXOffset,
        y: top + height / 2 + window.pageYOffset
      };
      break;
    case "center":
      point = {
        x: left + width / 2 + window.pageXOffset,
        y: top + height / 2 + window.pageYOffset
      };
      break;
    case "center right":
      point = {
        x: left + width + window.pageXOffset,
        y: top + height / 2 + window.pageYOffset
      };
      break;
    case "bottom left":
      point = {
        x: left + window.pageXOffset,
        y: top + height + window.pageYOffset
      };
      break;
    case "bottom center":
      point = {
        x: left + width / 2 + window.pageXOffset,
        y: top + height + window.pageYOffset
      };
      break;
    case "bottom right":
      point = {
        x: left + width + window.pageXOffset,
        y: top + height + window.pageYOffset
      };
      break;
  }
  return point;
};

Usage

  • getCoords(document.querySelector('selector'), 'center')

  • getCoords(document.querySelector('selector'), 'bottom right')

  • getCoords(document.querySelector('selector'), 'top center')

Raphael Rafatpanah
la source
2

Si cela ne vous dérange pas d'utiliser jQuery, vous pouvez utiliser offset()function. Reportez-vous à la documentation si vous souhaitez en savoir plus sur cette fonction.

Adam
la source