Encontrar a posição do elemento em relação ao documento

113

Qual é a maneira mais fácil de determinar a posição de um elemento em relação ao documento / corpo / janela do navegador?

Agora estou usando .offsetLeft/offsetTop , mas este método só fornece a posição relativa ao elemento pai, então você precisa determinar quantos pais para o elemento corpo, para saber a posição relativa ao corpo / janela do navegador / posição do documento.

Este método também é complicado.

einstein
fonte

Respostas:

58

Você pode percorrer offsetParentaté o nível superior do DOM.

function getOffsetLeft( elem )
{
    var offsetLeft = 0;
    do {
      if ( !isNaN( elem.offsetLeft ) )
      {
          offsetLeft += elem.offsetLeft;
      }
    } while( elem = elem.offsetParent );
    return offsetLeft;
}
KeatsKelleher
fonte
28
Não, não tem, quando qualquer pai (especialmente elemento html !!!) tem margens, preenchimentos ou bordas.
Flash Thunder de
em relação ao material de margem ... pode ajudar definir o tamanho da caixa para caixa de borda ... ou algo nesse sentido ... nada que não possa ser corrigido ...
Primeiro, você precisa decidir se deve levar em consideração a borda e a margem de acordo com o tamanho da caixa. Em segundo lugar, a margem colapsada deve ser considerada. E, por último, haverá uma situação mais complicada no futuro, o que tornará essa resposta ainda pior.
xianshenglu
6
Por que essa é a resposta aceita? Isso está desatualizado e tem um desempenho insatisfatório. Faça um favor a si mesmo e use getBoundingClientRect () conforme sugerido nas outras respostas.
Fabian von Ellerts
177

Você pode obter o topo e a esquerda sem atravessar o DOM desta forma:

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) };
}
manjericão
fonte
6
Essa resposta não leva em consideração as compensações dos pais. É apenas relativo à janela de visualização
Nickey
3
@Nickey isso não é verdade - a posição da janela de visualização, mais as verificações de deslocamento de rolagem, produzem as coordenadas relativas a todo o documento.
natchiketa
1
Pode ter erros em dispositivos móveis stackoverflow.com/a/32623832/962634 Precisa de pesquisa
manjericão
1
Por que você subtrai clientTop / clientLeft?
Teemoh
1
@Teemoh clientTop / clientLeft corresponde aos valores de borda no <html>elemento.
Raphael Rafatpanah
98

Você pode usar element.getBoundingClientRect()para recuperar a posição do elemento em relação à janela de visualização.

Em seguida, use document.documentElement.scrollToppara calcular o deslocamento da janela de visualização.

A soma dos dois dará a posição do elemento em relação ao documento:

element.getBoundingClientRect().top + document.documentElement.scrollTop
dy_
fonte
10
Em relação à janela de visualização não é o mesmo que em relação ao documento. Se a página for rolada um pouco para baixo, a parte superior em relação à janela de visualização será um número menor que a parte superior em relação ao documento.
MrVimes
14
ele começa em relação à janela de visualização e adiciona scrollY para obtê-lo em relação ao documento.
Joaquin Cuenca Abela
2
document.documentElement.scrollTopnão funciona no Safari, use em window.scrollYvez disso
Fabian von Ellerts de
7

Eu sugiro usar

element.getBoundingClientRect()

conforme proposto aqui, em vez do cálculo de deslocamento manual por meio de offsetLeft , offsetTop e offsetParent . conforme proposto aqui Em algumas circunstâncias * a passagem manual produz resultados inválidos. Veja este Plunker: http://plnkr.co/pC8Kgj

* Quando o elemento está dentro de um pai rolável com posicionamento estático (= padrão).

HANiS
fonte
Você também pode fazer o cálculo do deslocamento funcionar incorporando scrollLeft e scrollTop.
Ben J de
Mas eu encontrei offsetLefte offsetTopnão levo em consideração as transformações CSS3, o que getBoundingClientRect()leva. Portanto, esse é um caso em que os resultados podem ser inválidos. Se precisar, você pode obter o deslocamento de um elemento em relação ao pai (como offsetLeft) usando (element.getBoundingClientRect().left - parent.getBoundingClientRect().left).
Ben J de
deve ficar mais alto; esses valores são muito mais úteis e todos em uma chamada também!
mix3d
7

document-offset( Script de terceiros ) é interessante e parece aproveitar as abordagens das outras respostas aqui.

Exemplo:

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

Descobri que o método a seguir é o mais confiável ao lidar com casos extremos que deslocam offsetTop / offsetLeft.

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

Para aqueles que desejam obter as coordenadas xey de várias posições de um elemento, em relação ao documento.

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;
};

Uso

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

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

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

Raphael Rafatpanah
fonte
2

Se você não se importa em usar jQuery, pode usar a offset()função. Consulte a documentação se quiser ler mais sobre esta função.

Adão
fonte