Quero saber como obter a posição X e Y de elementos HTML como img
e div
em JavaScript em relação à janela do navegador.
Quero saber como obter a posição X e Y de elementos HTML como img
e div
em JavaScript em relação à janela do navegador.
Respostas:
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>');
<body>
.
element.getBoundingClientRect().top
não retorna o deslocamento superior correto no caso de um position: fixed
elemento 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: absolute
e posicioná-los na mesma posição como se fossem fixos, obterá os 200 pixels corretos. Eu não sei uma solução para isso.
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;
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
div
cujo centro usando CSS top:50%, left:50%; transform:translate(-50%, -50%);
... excelente. Concorde com a pergunta @ScottBiggs. Lógico é buscar a simplicidade.
rect
usando var
, let
ou const
. Caso contrário, é definido como window.rect
.
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
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á, offsetTop
digamos Y, em relação ao documento. A segunda linha retornará, offsetLeft
digamos, 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.
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).
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];
};
Você pode adicionar duas propriedades para Element.prototype
obter 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().top
e .left
: http://jsfiddle.net/ThinkingStiff/3G7EZ/
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.
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;
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');
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.
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.
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.
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.
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 };
}
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>
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
<svg>
elementos, por exemplo, não têm offsetLeft
, offsetTop
e offsetParent
propriedades.
DIV
ou IMG
nã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.
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 window
e documentElement
para 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;
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.
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
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);
/**
*
* @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.
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.
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 };
}
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()};
};
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;
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
el.offsetTop
e el.offsetLeft
(o OP não diz nada sobre jQuery ... Eu não sei por que sua resposta usa jQuery ou ...)