Recupere a posição (X, Y) de um elemento HTML em relação à janela do navegador

1570

Quero saber como obter a posição X e Y de elementos HTML como imge divem JavaScript em relação à janela do navegador.

Jack
fonte
134
Em relação a quê?
AnthonyWJones
4
eu estava usando essas 7 linhas de código que funcionam em todos os navegadores com discrepâncias em ie5,6,7 (não me lembrava de ter nenhum problema ... pode ser o tipo de documento) ... quirksmode.org/js/findpos. html e eu tenho usado muito por muitos anos. pode ser que alguém possa apontar as falhas, se houver.
precisa saber é o seguinte
98
@AnthonyWJones, suponho que você precisasse do prazer pedante, mas é óbvio, como sempre é quando não especificado, que o OP se refere ao caso mais geral ou às coordenadas da janela do navegador.
Motes 26/05
7
@Mote, não, não é tão óbvio. Deixe de lado a inferência, a subjetividade e os falsos axiomas. Pode ser relativo à viewport ou ao topo da página (também conhecido como document.documentElement).
Andre Figueiredo
15
A padronização para o mais geral não é inferência, é a progressão lógica, portanto seria relativa à janela ou "topo da página" pode ser o termo que você coloca. Na teoria dos jogos, é codificado como um conceito chamado ponto de Schelling. Certifique-se de especificar quando não se refere ao caso mais geral.
Motes 22/10

Respostas:

1796

A abordagem correta é usar element.getBoundingClientRect():

var rect = element.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);

O Internet Explorer oferece suporte a isso desde que você provavelmente se preocupe e, finalmente, foi padronizado nas Visualizações CSSOM . Todos os outros navegadores o adotaram há muito tempo .

Alguns navegadores também retornam propriedades de altura e largura, embora isso não seja padrão. Se você está preocupado com a compatibilidade do navegador mais antigo, verifique as revisões desta resposta para obter uma implementação degradada otimizada.

Os valores retornados por element.getBoundingClientRect()são relativos à viewport. Se você precisar dele em relação a outro elemento, basta subtrair um retângulo do outro:

var bodyRect = document.body.getBoundingClientRect(),
    elemRect = element.getBoundingClientRect(),
    offset   = elemRect.top - bodyRect.top;

alert('Element is ' + offset + ' vertical pixels from <body>');
Andy E
fonte
143
Um artigo muito interessante sobre coordenadas JS . Este artigo é mencionado nenhum lugar no SO, deveria ..
E2-E4
6
Não funciona ... são 8 pixels na vertical e na horizontal, devido à margem de 8px no corpo. A solução de Meouw funciona perfeitamente. Se você quiser, tenho um pequeno teste para demonstrar o problema.
CpnCrunch
3
Na verdade, acho que depende do que você realmente deseja obter. A questão é um pouco ambígua. Sua resposta está correta se você deseja apenas as cordas relativas à viewport ou ao elemento body, mas isso não ajuda no caso em que você deseja a posição absoluta do elemento (que eu imagino ser o que a maioria das pessoas deseja) . A resposta de meouw também não fornece isso, a menos que você remova os bits '-scrollLeft' e '-scrollTop'.
CpnCrunch #
4
@CpnCrunch: Entendo o que você quer dizer agora; Não sabia que você estava se referindo à parte final da minha resposta. Quando o corpo tem uma margem, sua caixa delimitadora será deslocada pelo número de pixels em cada lado respectivo. Nesse caso, você também precisará subtrair o estilo da margem calculada das coordenadas do corpo. Vale ressaltar que as redefinições de CSS removem a margem de <body>.
Andy E
3
element.getBoundingClientRect().topnão retorna o deslocamento superior correto no caso de um position: fixedelemento no iOS (iPhone 8) se ele contiver um elemento de entrada focado e o teclado virtual deslizar para cima. Por exemplo, se o deslocamento real da parte superior da janela for 200px, o relatório será 420px, onde 220px é a altura do teclado virtual. Se você definir elementos position: absolutee posicioná-los na mesma posição como se fossem fixos, obterá os 200 pixels corretos. Eu não sei uma solução para isso.
Jānis Elmeris
327

As bibliotecas se esforçam para obter compensações precisas para um elemento.
aqui está uma função simples que faz o trabalho em todas as circunstâncias que eu tentei.

function getOffset( el ) {
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
        _x += el.offsetLeft - el.scrollLeft;
        _y += el.offsetTop - el.scrollTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}
var x = getOffset( document.getElementById('yourElId') ).left; 
meouw
fonte
14
alterar: el = el.parentNode para: el = el.offsetParent; e parece funcionar para iframes aninhados agora ... Estou pensando que era isso que você pretendia?
297 Adam
4
Esta solução está incompleta. Tente colocar uma borda grossa em uma div e aninhe-a algumas vezes. Em qualquer versão do Firefox (2-5), ela será desabilitada pela (s) largura (s) da borda (mesmo no modo de conformidade com os padrões).
ck_
3
não existe uma maneira mais limpa de fazer isso, como as funções el.totalOffsetLeft e totalOffsetTop? O jQuery certamente tem essa opção, mas é uma pena que não exista um método oficial para isso, precisamos aguardar o DOM4? Como estou construindo um aplicativo de tela HTML5, acho que a melhor solução seria inserir um elemento de tela em um iframe para que eu possa obter coordenadas reais com clientX e clientY quando clicamos no elemento.
Baptx # 30/12
1
Então, @ Adam e meouw, você está dizendo que o primeiro bloco de código está errado? Então por que não remover isso completamente? (Esta questão tem visto bastante alguns espectadores ao longo dos anos; pode ser bom para as coisas remover, se eles estão errados?)
Arjan
2
@baptx: Eu sei que esta é uma resposta tardia, mas não vi seu comentário antes. Veja minha resposta abaixo para obter uma alternativa mais compatível com os padrões - você nem precisa realmente do código de fallback, pois os navegadores o suportam há muito tempo.
Andy E
242

Isso funcionou para mim (modificado da resposta mais votada):

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

Usando isso, podemos chamar

getOffset(element).left

ou

getOffset(element).top
Adam Grant
fonte
1
Apenas esta solução funciona para mim quando o elemento é colocado em divcujo centro usando CSS top:50%, left:50%; transform:translate(-50%, -50%);... excelente. Concorde com a pergunta @ScottBiggs. Lógico é buscar a simplicidade.
nelek 03/09/15
3
talvez não é um vencedor causa é o mesmo que a solução de Andy E, mas não funciona em navegadores antigos <IE9
niltoid
getBoundingClientRect provoca uma enorme repaint pico em meus gráficos de desempenho
frumbert
1
Você deve definir rectusando var, letou const. Caso contrário, é definido como window.rect.
hev1
2
left: rect.left + window.scrollX + (rect.width / 2), top: rect.top + window.scrollY + (rect.height / 2)irá retornar a posição média de um elemento
abhishake
97

Se você quiser que seja feito apenas em javascript , aqui estão alguns liners usandogetBoundingClientRect()

window.scrollY + document.querySelector('#elementId').getBoundingClientRect().top // Y

window.scrollX + document.querySelector('#elementId').getBoundingClientRect().left // X

A primeira linha retornará, offsetTopdigamos Y, em relação ao documento. A segunda linha retornará, offsetLeftdigamos, X em relação ao documento.

getBoundingClientRect() é uma função javascript que retorna a posição do elemento em relação à janela de visualização da janela.

Mustkeem K
fonte
2
Essa deve ser a solução. Não há código bs longo aqui, é compreensível e muito preciso!
E Alexis MT
2
e se o elemento estiver dentro de um elemento rolável? A única maneira de fazer isso é para resumir offsetTop de todos os elementos pai, como outras respostas sugerem
Dmitri Pisarev
Como Dmitri afirma, isso é incorreto e muito defeituoso. Não deve ter tantos votos positivos quanto ele. SEMPRE estará também em um elemento rolável porque o próprio documento pode ser rolado. Em outras palavras, a menos que o documento inteiro caiba na janela de visualização, ele sempre estará incorreto se o usuário rolou a janela principal.
Lev
46

Os elementos HTML na maioria dos navegadores terão: -

offsetLeft
offsetTop

Eles especificam a posição do elemento em relação ao pai mais próximo que possui layout. Esse pai geralmente pode ser acessado pela propriedade offsetParent.

IE e FF3 têm

clientLeft
clientTop

Essas propriedades são menos comuns, elas especificam uma posição de elementos com a área do cliente pai (a área acolchoada faz parte da área do cliente, mas a borda e a margem não).

AnthonyWJones
fonte
36

Se a página incluir, pelo menos, qualquer "DIV", a função fornecida por meouw lançará o valor "Y" além dos limites da página atual. Para encontrar a posição exata, você precisa lidar com os offsetParent e parentNode.

Experimente o código fornecido abaixo (está marcado para FF2):


var getAbsPosition = function(el){
    var el2 = el;
    var curtop = 0;
    var curleft = 0;
    if (document.getElementById || document.all) {
        do  {
            curleft += el.offsetLeft-el.scrollLeft;
            curtop += el.offsetTop-el.scrollTop;
            el = el.offsetParent;
            el2 = el2.parentNode;
            while (el2 != el) {
                curleft -= el2.scrollLeft;
                curtop -= el2.scrollTop;
                el2 = el2.parentNode;
            }
        } while (el.offsetParent);

    } else if (document.layers) {
        curtop += el.y;
        curleft += el.x;
    }
    return [curtop, curleft];
};
Refik Ayata
fonte
1
como nas outras soluções, mesmo com o FF 24.4, o código acima não funciona quando as larguras das bordas existem como parte do layout de posicionamento.
rob
Você é um salva-vidas. Estou trabalhando com alguns erros profundamente profundos do GWT agora e jogar isso em um método JSNI resolve todos os meus problemas!
Will Byrne
35

Você pode adicionar duas propriedades para Element.prototypeobter a parte superior / esquerda de qualquer elemento.

Object.defineProperty( Element.prototype, 'documentOffsetTop', {
    get: function () { 
        return this.offsetTop + ( this.offsetParent ? this.offsetParent.documentOffsetTop : 0 );
    }
} );

Object.defineProperty( Element.prototype, 'documentOffsetLeft', {
    get: function () { 
        return this.offsetLeft + ( this.offsetParent ? this.offsetParent.documentOffsetLeft : 0 );
    }
} );

Isso é chamado assim:

var x = document.getElementById( 'myDiv' ).documentOffsetLeft;

Aqui está uma demonstração comparando os resultados com o jQuery offset().tope .left: http://jsfiddle.net/ThinkingStiff/3G7EZ/

ThinkingStiff
fonte
14
modificar Element.prototypeé geralmente considerado uma má ideia . Isso leva a um código muito difícil de manter. Além disso, esse código não conta para rolagem.
Jared Forsyth
23

Para recuperar a posição relativa à página com eficiência e sem usar uma função recursiva: (inclui o IE também)

var element = document.getElementById('elementId'); //replace elementId with your element's Id.
var rect = element.getBoundingClientRect();
var elementLeft,elementTop; //x and y
var scrollTop = document.documentElement.scrollTop?
                document.documentElement.scrollTop:document.body.scrollTop;
var scrollLeft = document.documentElement.scrollLeft?                   
                 document.documentElement.scrollLeft:document.body.scrollLeft;
elementTop = rect.top+scrollTop;
elementLeft = rect.left+scrollLeft;
Abdul Rahim Haddad
fonte
Incluir o importante scrollTop / scrollLeft torna essa resposta um pouco mais correta.
scunliffe
19

Que tal algo assim, passando o ID do elemento e ele retornará à esquerda ou no topo, também podemos combiná-los:

1) encontre à esquerda

function findLeft(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return rec.left + window.scrollX;
} //call it like findLeft('#header');

2) encontre o topo

function findTop(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return rec.top + window.scrollY;
} //call it like findTop('#header');

ou 3) encontre a esquerda e a parte superior juntas

function findTopLeft(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return {top: rec.top + window.scrollY, left: rec.left + window.scrollX};
} //call it like findTopLeft('#header');
Alireza
fonte
16

Você pode ser melhor atendido usando uma estrutura JavaScript, que possui funções para retornar essas informações (e muito mais!) De maneira independente do navegador. Aqui estão alguns:

Com essas estruturas, você pode fazer algo como: $('id-of-img').top obter a coordenada de pixel y da imagem.

Shalom Craimer
fonte
2
@ Costa Todos eles usam esse tipo de função, embora esse seja um campo em evolução, pois navegadores diferentes se adaptam de maneira diferente à especificação. Grande parte do código lida com "consertar" coisas que dão errado. Você realmente deve olhar o código fonte.
Shalom Craimer
18
@ scraimer: jQuery e YUI NÃO são estruturas !!! Estas são bibliotecas ! Não é o mesmo. Citando jquery.com : "O jQuery é uma biblioteca JavaScript rápida, pequena e rica em recursos ". Citando yuilibrary.com (você também pode ver a palavra "mágica" no URL ...): "YUI é uma biblioteca JavaScript e CSS de código aberto gratuita para criar aplicativos da Web altamente interativos".
Sk8erPeter
29
Então você deve usar bibliotecas enormes apenas para esta tarefa simples? Não é necessário. Eu odeio que toda vez que quero fazer algo com js, recebo essas soluções jQuery. jQuery não é JS!
Opptatt Jobber
1
@OpptattJobber Concordo ... JQuery não é javascript. Ele se baseia em Javascript, mas é uma linguagem de programação completamente diferente.
Nick Manning
5
@NickManning Não é um idioma novo, é uma camada de abstração para o DOM que elimina a necessidade de escrever soluções de compatibilidade e desempenho diretamente no seu código. Se você não usa o jQuery, deve estar usando algum tipo de camada de abstração.
Ginman
15

jQuery .offset () obterá as coordenadas atuais do primeiro elemento ou definirá as coordenadas de cada elemento, no conjunto de elementos correspondentes, em relação ao documento.

akauppi
fonte
Por alguma razão, não está dando os mesmos resultados no IE em comparação com outros navegadores. Eu acho que no IE está dando posição relativa para a janela, por isso, se você rolar, você vai ter resultados diferentes no IE em relação aos outros
Abdul Rahim Haddad
1
offset () é confundido pelo zoom, pelo menos no Android Chrome, por isso é inútil. stackoverflow.com/a/11396681/1691517 mostra uma maneira tolerante ao zoom.
Timo Kähkönen
10

Peguei a resposta de @ meouw, adicionei o clientLeft que permite a borda e criei três versões:

getAbsoluteOffsetFromBody - semelhante ao @ meouw, obtém a posição absoluta em relação ao corpo ou ao elemento html do documento (dependendo do modo quirks)

getAbsoluteOffsetFromGivenElement - retorna a posição absoluta em relação ao elemento especificado (relatedEl). Observe que o elemento fornecido deve conter o elemento el, ou ele se comportará da mesma maneira que getAbsoluteOffsetFromBody. Isso é útil se você tiver dois elementos contidos em outro elemento (conhecido) (opcionalmente vários nós na árvore de nós) e desejar torná-los na mesma posição.

getAbsoluteOffsetFromRelative - retorna a posição absoluta relativa ao primeiro elemento pai com a posição: relativa. Isso é semelhante ao getAbsoluteOffsetFromGivenElement, pelo mesmo motivo, mas irá apenas até o primeiro elemento correspondente.

getAbsoluteOffsetFromBody = function( el )
{   // finds the offset of el from the body or html element
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}

getAbsoluteOffsetFromGivenElement = function( el, relativeEl )
{   // finds the offset of el from relativeEl
    var _x = 0;
    var _y = 0;
    while( el && el != relativeEl && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}

getAbsoluteOffsetFromRelative = function( el )
{   // finds the offset of el from the first parent with position: relative
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
        if (el != null)
        {
            if (getComputedStyle !== 'undefined')
                valString = getComputedStyle(el, null).getPropertyValue('position');
            else
                valString = el.currentStyle['position'];
            if (valString === "relative")
                el = null;
        }
    }
    return { top: _y, left: _x };
}

Se você ainda está tendo problemas, principalmente em relação à rolagem, tente acessar http://www.greywyvern.com/?post=331 - Observei pelo menos um pedaço de código questionável no getStyle, o que deve ser bom se os navegadores se comportarem , mas não testou o resto.

James Carlyle-Clarke
fonte
Interessante ... mas no Edge, getAbsoluteOffsetFromBody (elemento) .top retorna um valor diferente enquanto eu navego, no Chrome ele permanece consistente enquanto eu navego. Parece que o Edge está se ajustando à posição de rolagem. Quando o elemento é deslocado para fora da vista, recebo um valor negativo.
Norman
getAbsoluteOffsetFromRelative fez o meu dia, tive que reproduzir a dica da ferramenta de autoinicialização sem o Javascript da autoinicialização, isso me ajudou muito.
Nolyurn 13/08/19
Caso o Meouw mude seu nome de usuário, vincule-o para responder: stackoverflow.com/a/442474/3654837 . (Eu não quero esbarrar neste tópico antigo, então sim, eu coloquei um comentário)
Mukyuu
8

se estiver usando jQuery, o plugin de dimensões é excelente e permite que você especifique exatamente o que deseja.

por exemplo

Posição relativa, posição absoluta, posição absoluta sem preenchimento, com preenchimento ...

Continua, digamos que há muito que você pode fazer com isso.

Além disso, o bônus de usar o jQuery é o tamanho de arquivo leve e o uso fácil; você não voltará para o JavaScript sem ele posteriormente.

John_
fonte
8

Este é o melhor código que eu consegui criar (funciona também em iframes, ao contrário do offset do jQuery ()). Parece que o webkit tem um comportamento um pouco diferente.

Com base no comentário de meouw:

function getOffset( el ) {
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
        _x += el.offsetLeft - el.scrollLeft;
        _y += el.offsetTop - el.scrollTop;
        // chrome/safari
        if ($.browser.webkit) {
            el = el.parentNode;
        } else {
            // firefox/IE
            el = el.offsetParent;
        }
    }
    return { top: _y, left: _x };
}
Ron Reiter
fonte
Observe que você precisará jQueryusar $.browser.webkit; Você precisará brincar navigator.userAgentpara fazer o mesmo com o puro JavaScript.
SP
2
$ .browser não está mais disponível na versão mais recente do jQuery
Gus
infelizmente, mesmo com o FF 24.4, o código acima não funciona quando as larguras das bordas existem como parte do layout de posicionamento.
rob
8

Se você estiver usando jQuery, esta poderia ser uma solução simples:

<script>
  var el = $("#element");
  var position = el.position();
  console.log( "left: " + position.left + ", top: " + position.top );
</script>
Adam Boostani
fonte
8

Diferença entre pequeno e pequeno

function getPosition( el ) {
    var x = 0;
    var y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
    x += el.offsetLeft - el.scrollLeft;
    y += el.offsetTop - el.scrollTop;
    el = el.offsetParent;
    }
    return { top: y, left: x };
}

Veja um exemplo de coordenadas: http://javascript.info/tutorial/coordinates

KingRider
fonte
Infelizmente, isso não funciona para todos os elementos encontrados em um documento HTML moderno. Incorporados <svg>elementos, por exemplo, não têm offsetLeft, offsetTope offsetParentpropriedades.
Jonathan Eunice
É justo DIVou IMGnão SVG. Exibir "Veja um exemplo de coordenadas"? você pode encontrar o objeto svg na posição call getOffsetRect , tentar e depender do trabalho do objeto.
21416 KingRider
7

A abordagem mais limpa que encontrei é uma versão simplificada da técnica usada pelo jQuery offset. Semelhante a algumas das outras respostas com que começa getBoundingClientRect; Em seguida, usa o windowe documentElementpara ajustar a posição de rolagem, bem como coisas como a margem na body(muitas vezes o padrão).

var rect = el.getBoundingClientRect();
var docEl = document.documentElement;

var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;

James Montagne
fonte
6

Embora seja provável que isso se perca na base de tantas respostas, as principais soluções aqui não estavam funcionando para mim.
Até onde eu sabia, nenhuma das outras respostas teria ajudado.

Situação :
em uma página HTML5, eu tinha um menu que era um elemento nav dentro de um cabeçalho (não o cabeçalho THE, mas um cabeçalho em outro elemento).
Eu queria que a navegação ficasse no topo assim que um usuário rolasse para ela, mas antes disso o cabeçalho estava em posição absoluta (para que eu pudesse sobrepor algo mais).
As soluções acima nunca acionaram uma mudança porque .offsetTop não mudaria, pois era um elemento posicionado absoluto. Além disso, a propriedade .scrollTop era simplesmente a parte superior do elemento mais ... ... ou seja, 0 e sempre seria 0.
Quaisquer testes que eu executei utilizando esses dois (e o mesmo com os resultados getBoundingClientRect) não me diriam se a parte superior da barra de navegação rolou para a parte superior da página visível (novamente, conforme relatado no console, eles simplesmente permaneceram os mesmos números ao rolar ocorreu).

Solução
A solução para mim estava utilizando

window.visualViewport.pageTop

O valor da propriedade pageTop reflete a seção visível da tela, permitindo-me rastrear onde um elemento está em referência aos limites da área visível.

Provavelmente desnecessário dizer que, sempre que estou lidando com rolagem, espero usar esta solução para responder programaticamente ao movimento dos elementos que estão sendo rolados.
Espero que isso ajude mais alguém.
NOTA IMPORTANTE: Parece funcionar atualmente no Chrome e no Opera e definitivamente não no Firefox (6-2018) ... até o Firefox suportar o visualViewport, eu recomendo NÃO usar esse método (e espero que eles façam isso em breve ... faz muito mais sentido do que o resto).


ATUALIZAÇÃO:
Apenas uma observação sobre esta solução.
Embora eu ainda ache o que descobri ser muito valioso para situações em que "... responda programaticamente ao movimento de elementos sendo rolados". é aplicável. A melhor solução para o problema que tive foi usar CSS para definir posição: pegajosano elemento Usando o sticky, você pode fazer com que um elemento fique no topo sem usar javascript (OBSERVAÇÃO: há momentos em que isso não funcionará tão efetivamente quanto alterar o elemento para fixo, mas para a maioria dos usos, a abordagem do sticky provavelmente será superior)

UPDATE01:
Então percebi que para uma página diferente, eu tinha um requisito em que precisava detectar a posição de um elemento em uma configuração de rolagem levemente complexa (paralaxe mais elementos que rolam como parte de uma mensagem). Percebi naquele cenário que o seguinte fornecia o valor que utilizava para determinar quando fazer algo:

  let bodyElement = document.getElementsByTagName('body')[0];
  let elementToTrack = bodyElement.querySelector('.trackme');
  trackedObjPos = elementToTrack.getBoundingClientRect().top;
  if(trackedObjPos > 264)
  {
    bodyElement.style.cssText = '';
  }

Espero que esta resposta seja mais amplamente útil agora.

MER
fonte
isso é muito provável que seja no topo de tantas respostas quando você clica ativo para classificar as respostas
Lucchi
@ lucchi: D, bem, eu suponho que eu poderia remover o texto @ the top ... embora eu tenha acontecido com este novamente e percebi que havia encontrado uma solução melhor para o meu próprio problema ... embora minha resposta seja mais nota de rodapé para as respostas mais completas. Eu suspeito que meus requisitos talvez não fossem tão comuns que as outras respostas não funcionaram para mim?
MER
1
De qualquer forma, agradeço a sua resposta, foi apenas para lhe dizer que, qualquer pessoa que se interessar estará ciente de que você escreveu algo novo. Você não está sozinho ...
lucchi
5

Eu fiz isso assim, para ter compatibilidade cruzada com navegadores antigos.

// For really old browser's or incompatible ones
    function getOffsetSum(elem) {
        var top = 0,
            left = 0,
            bottom = 0,
            right = 0

         var width = elem.offsetWidth;
         var height = elem.offsetHeight;

        while (elem) {
            top += elem.offsetTop;
            left += elem.offsetLeft;
            elem = elem.offsetParent;
        }

         right = left + width;
         bottom = top + height;

        return {
            top: top,
            left: left,
            bottom: bottom,
            right: right,
        }
    }

    function getOffsetRect(elem) {
        var box = elem.getBoundingClientRect();

        var body = document.body;
        var docElem = document.documentElement;

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

        var clientTop = docElem.clientTop;
        var clientLeft = docElem.clientLeft;


        var top = box.top + scrollTop - clientTop;
        var left = box.left + scrollLeft - clientLeft;
        var bottom = top + (box.bottom - box.top);
        var right = left + (box.right - box.left);

        return {
            top: Math.round(top),
            left: Math.round(left),
            bottom: Math.round(bottom),
            right: Math.round(right),
        }
    }

    function getOffset(elem) {
        if (elem) {
            if (elem.getBoundingClientRect) {
                return getOffsetRect(elem);
            } else { // old browser
                return getOffsetSum(elem);
            }
        } else
            return null;
    }

Mais sobre coordenadas em JavaScript aqui: http://javascript.info/tutorial/coordinates

Bojan Kseneman
fonte
4

Para obter o deslocamento total de um elemento, você pode resumir recursivamente todas as compensações pai:

function getParentOffsets(el): number {
if (el.offsetParent) {
    return el.offsetParent.offsetTop + getParentOffset(el.offsetParent);
} else {
    return 0;
}
}

com esta função de utilitário, o deslocamento superior total de um elemento dom é:

el.offsetTop + getParentOffsets(el);
Kevin K.
fonte
3
/**
 *
 * @param {HTMLElement} el
 * @return {{top: number, left: number}}
 */
function getDocumentOffsetPosition(el) {
    var position = {
        top: el.offsetTop,
        left: el.offsetLeft
    };
    if (el.offsetParent) {
        var parentPosition = getDocumentOffsetPosition(el.offsetParent);
        position.top += parentPosition.top;
        position.left += parentPosition.left;
    }
    return position;
}

Agradeço à ThinkingStiff pela resposta , esta é apenas outra versão.

Văn Quyết
fonte
2

Usei com sucesso a solução de Andy E para posicionar um modal de bootstrap 2, dependendo do link em uma linha da tabela em que um usuário clica. A página é uma página Tapeçaria 5 e o javascript abaixo é importado na classe de página java.

javascript:

function setLinkPosition(clientId){
var bodyRect = document.body.getBoundingClientRect(),
elemRect = clientId.getBoundingClientRect(),
offset   = elemRect.top - bodyRect.top;
offset   = offset + 20;
$('#serviceLineModal').css("top", offset);

}

Meu código modal:

<div id="serviceLineModal" class="modal hide fade add-absolute-position" data-backdrop="static" 
 tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="top:50%;">
<div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
    <h3 id="myModalLabel">Modal header</h3>
</div>

<div class="modal-body">
    <t:zone t:id="modalZone" id="modalZone">
        <p>You selected service line number: ${serviceLineNumberSelected}</p>
    </t:zone>
</div>

<div class="modal-footer">
    <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
    <!-- <button class="btn btn-primary">Save changes</button> -->
</div>

O link no loop:

<t:loop source="servicesToDisplay" value="service" encoder="encoder">
<tr style="border-right: 1px solid black;">       
    <td style="white-space:nowrap;" class="add-padding-left-and-right no-border"> 
        <a t:type="eventLink" t:event="serviceLineNumberSelected" t:context="service.serviceLineNumber" 
            t:zone="pageZone" t:clientId="modalLink${service.serviceLineNumber}"
            onmouseover="setLinkPosition(this);">
            <i class="icon-chevron-down"></i> <!-- ${service.serviceLineNumber} -->
        </a>
    </td>

E o código java na classe da página:

void onServiceLineNumberSelected(String number){
    checkForNullSession();
    serviceLineNumberSelected = number;
    addOpenServiceLineDialogCommand();
    ajaxResponseRenderer.addRender(modalZone);
}

protected void addOpenServiceLineDialogCommand() {
    ajaxResponseRenderer.addCallback(new JavaScriptCallback() {
        @Override
        public void run(JavaScriptSupport javascriptSupport) {
            javascriptSupport.addScript("$('#serviceLineModal').modal('show');");
        }
    });
}

Espero que isso ajude alguém, este post ajudou.

Mark Espinoza
fonte
Esse código js me ajudou, eu tenho um aplicativo antigo de formulários da web usando vários espaços reservados para injetar páginas .aspx e não consegui descobrir como anexarChild () de uma caixa pop-up que eu estava criando para colocá-lo na janela de exibição porque toda a árvore do DOM está danificada com vários espaços reservados e marcação incorreta. Usar o body.getBoundingClientRect () e usar o topo no posicionamento era o que eu precisava. Obrigado.
Brad Martin
1

Depois de muita pesquisa e teste, isso parece funcionar

function getPosition(e) {
    var isNotFirefox = (navigator.userAgent.toLowerCase().indexOf('firefox') == -1);
    var x = 0, y = 0;
    while (e) {
        x += e.offsetLeft - e.scrollLeft + (isNotFirefox ? e.clientLeft : 0);
        y += e.offsetTop - e.scrollTop + (isNotFirefox ? e.clientTop : 0);
        e = e.offsetParent;
    }
    return { x: x + window.scrollX, y: y + window.scrollY };
}

consulte http://jsbin.com/xuvovalifo/edit?html,js,output

kernowcode
fonte
1

Só pensei em jogar isso lá fora também.
Não pude testá-lo em navegadores mais antigos, mas funciona nos mais recentes dos principais 3. :)

Element.prototype.getOffsetTop = function() {
    return ( this.parentElement )? this.offsetTop + this.parentElement.getOffsetTop(): this.offsetTop;
};
Element.prototype.getOffsetLeft = function() {
    return ( this.parentElement )? this.offsetLeft + this.parentElement.getOffsetLeft(): this.offsetLeft;
};
Element.prototype.getOffset = function() {
    return {'left':this.getOffsetLeft(),'top':this.getOffsetTop()};
};
Duncan
fonte
0

Como navegadores diferentes estão processando bordas, preenchimentos, margens e etc. de maneira diferente. Eu escrevi uma pequena função para recuperar as posições superior e esquerda de um elemento específico em cada elemento raiz que você deseja na dimensão precisa:

function getTop(root, offset) {
    var rootRect = root.getBoundingClientRect();
    var offsetRect = offset.getBoundingClientRect();
    return offsetRect.top - rootRect.top;
}

Para recuperar a posição esquerda, você deve retornar:

    return offsetRect.left - rootRect.left;
samadadi
fonte
-1

Obter posição de div em relação à esquerda e na parte superior

  var elm = $('#div_id');  //get the div
  var posY_top = elm.offset().top;  //get the position from top
  var posX_left = elm.offset().left; //get the position from left 
Vishal
fonte
I para baixo votado porque parte inferior e direita não são propriedades de deslocamento ()
Macroman
@ MacroMan, eu respeito sua decisão, mas já expliquei no texto que "o código inferior e direito não foi testado". Além disso, vou atualizar meu código muito em breve.
Vishal
Eu acho que é el.offsetTope el.offsetLeft(o OP não diz nada sobre jQuery ... Eu não sei por que sua resposta usa jQuery ou ...)
mesqueeb