Como obter a posição superior de um elemento em relação à janela de visualização do navegador?


173

Desejo obter a posição de um elemento em relação à viewport do navegador (a viewport na qual a página é exibida, não a página inteira). Como isso pode ser feito em JavaScript?

Muito Obrigado


2
Penso que esta pergunta é semelhante ao stackoverflow.com/questions/211703/…, que tem uma solução muito boa.
— Jakob Runge

1
@JakobRunge, Isso foi "legal" em 2013?
— Iulian Onofrei

1
Considere aceitar uma solução.
— Iulian Onofrei

Respostas:


305

As respostas existentes agora estão desatualizadas. O getBoundingClientRect()método nativo já existe há algum tempo e faz exatamente o que a pergunta pede. Além disso, ele é suportado em todos os navegadores (incluindo o IE 5, ao que parece!)

Na página MDN :

O valor retornado é um objeto TextRectangle, que contém propriedades esquerda, superior, direita e inferior somente leitura que descrevem a caixa de borda, em pixels, com a parte superior esquerda relativa à parte superior esquerda da janela de visualização .

Você usa assim:

var viewportOffset = el.getBoundingClientRect();
// these are relative to the viewport, i.e. the window
var top = viewportOffset.top;
var left = viewportOffset.left;

3
Isso pode não funcionar corretamente quando o zoom do navegador é usado (Android Chrome). A solução do @rism ( veja abaixo ) funciona nesse caso
— Dmitry

4
Bom para a maioria dos casos, mas assume que o elemento não está dentro de um iframe incorporado, certo? A pergunta está sendo feita sobre a janela do navegador. O el.getBoundingClientRect () retornará relativo à janela do iframe, não à janela do navegador.
— cakidnyc

6
A pergunta está sendo feita sobre a janela do navegador.
— Dmitry Dvornikov

2
@Denilson getBoundingClientRect().topnão está no meu IE11, retorna 0. Você tem alguma solução.
— Arif

@Arif: Desculpe, mas não consigo reproduzir seu problema. Eu testei isso no IE11 e ainda tem resultados: codepen.io/denilsonsa/pen/ypqyXj
— Denilson Sá Maia

57

No meu caso, apenas para garantir a rolagem, adicionei o window.scroll à equação:

var element = document.getElementById('myElement');
var topPos = element.getBoundingClientRect().top + window.scrollY;
var leftPos = element.getBoundingClientRect().left + window.scrollX;

Isso me permite obter a posição relativa real do elemento no documento, mesmo que ele tenha sido rolado.


2
Você não deveria adicionar a posição de rolagem em vez de subtraí-la?
— Iulian Onofrei

Votei na resposta, mas ainda assim, @lulian Onofrei está certo. Por favor edite.
— Pmrotule 17/08/19

O @Fabio getBoundingClientRect().topnão está no meu IE11, ele retorna 0. Você tem alguma solução.
— Arif 12/01

1
@Arif window.scrollY || window.pageYOffsetpode melhorar o suporte
— Fabian von Ellerts

@FabianvonEllerts :)
— Arif

15

Editar: adicione algum código para explicar a rolagem da página.

function findPos(id) {
    var node = document.getElementById(id);     
    var curtop = 0;
    var curtopscroll = 0;
    if (node.offsetParent) {
        do {
            curtop += node.offsetTop;
            curtopscroll += node.offsetParent ? node.offsetParent.scrollTop : 0;
        } while (node = node.offsetParent);

        alert(curtop - curtopscroll);
    }
}

O argumento id é o id do elemento cujo deslocamento você deseja. Adaptado de uma postagem de modo quirks .


1
Obrigado pela sua resposta, mas acho que você não entendeu minha pergunta, sei como obter o topo de um elemento em relação à página, o que estou tentando fazer é obter a posição em relação à 'viewport' (por exemplo, a janela do navegador , não o documento)
— Waleed Eissa

Desculpe, o seu uso de 'viewport' me impressionou. Então, você está dizendo que deseja dar conta do cromo do navegador, ou seja, barra de favoritos, barra de localização etc.?
— Derek Swingley

Não, apenas a janela através da qual você vê a página. Se a página for rolada, será diferente da parte superior do documento; caso contrário, serão iguais.
— Waleed Eissa

Oh ok eu entendo. Não tenho certeza de como fazer isso ... editará minha resposta se surgir alguma coisa.
— Derek Swingley

Eu fiz o código acima um pouco mais feio, mas funciona ... use offsetParent.scroll #
— Derek Swingley

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

Isso parece calcular a partir do topo do documento, não da janela de exibição
— Scott Leonard

6

Podes tentar:

node.offsetTop - window.scrollY

Funciona no Opera com a meta tag da viewport definida.


7
Se eu entendi a documentação corretamente, offsetTopé relativa ao elemento posicionado mais próximo. Que, dependendo da estrutura da página, pode ou não ser elemento raiz.
— Denilson Sá Maia

5

O jQuery implementa isso de maneira bastante elegante. Se você procurar a fonte para jQuery offset, verá que é basicamente assim que é implementado:

var rect = elem.getBoundingClientRect();
var win = elem.ownerDocument.defaultView;

return {
    top: rect.top + win.pageYOffset,
    left: rect.left + win.pageXOffset
};

2

A função nesta página retornará um retângulo com as coordenadas superior, esquerda, altura e largura de um elemento passado em relação à porta de visualização do navegador.

    localToGlobal: function( _el ) {
       var target = _el,
       target_width = target.offsetWidth,
       target_height = target.offsetHeight,
       target_left = target.offsetLeft,
       target_top = target.offsetTop,
       gleft = 0,
       gtop = 0,
       rect = {};

       var moonwalk = function( _parent ) {
        if (!!_parent) {
            gleft += _parent.offsetLeft;
            gtop += _parent.offsetTop;
            moonwalk( _parent.offsetParent );
        } else {
            return rect = {
            top: target.offsetTop + gtop,
            left: target.offsetLeft + gleft,
            bottom: (target.offsetTop + gtop) + target_height,
            right: (target.offsetLeft + gleft) + target_width
            };
        }
    };
        moonwalk( target.offsetParent );
        return rect;
}

2
function inViewport(element) {
    let bounds = element.getBoundingClientRect();
    let viewWidth = document.documentElement.clientWidth;
    let viewHeight = document.documentElement.clientHeight;

    if (bounds['left'] < 0) return false;
    if (bounds['top'] < 0) return false;
    if (bounds['right'] > viewWidth) return false;
    if (bounds['bottom'] > viewHeight) return false;

    return true;
}

fonte


1

Obrigado por todas as respostas. Parece que o Prototype já possui uma função que faz isso (a função page ()). Ao visualizar o código fonte da função, descobri que ela primeiro calcula a posição do deslocamento do elemento em relação à página (ou seja, a parte superior do documento) e depois subtrai o scrollTop a partir dela. Veja o código fonte do protótipo para mais detalhes.


Boa decisão. Provavelmente é melhor usar uma das principais bibliotecas, pois elas têm mais chances de obter o mesmo resultado nos navegadores. Se você estiver curioso, verifique minha resposta. O código fará isso, mas não tenho idéia de como ele se sustentará nos navegadores.
— Derek Swingley

1

Estou assumindo que btn1existe um elemento com um ID na página da Web e também que o jQuery está incluído. Isso funcionou em todos os navegadores modernos do Chrome, FireFox, IE> = 9 e Edge. O jQuery está sendo usado apenas para determinar a posição em relação ao documento.

var screenRelativeTop =  $("#btn1").offset().top - (window.scrollY || 
                                            window.pageYOffset || document.body.scrollTop);

var screenRelativeLeft =  $("#btn1").offset().left - (window.scrollX ||
                                           window.pageXOffset || document.body.scrollLeft);

1
jQuery é JavaScript. Acredito que você quis dizer "não é 100% baunilha / JS puro".
— Hungerstar 12/04/19

Sim, foi o que eu quis dizer.
— Sunil

1

Às vezes, getBoundingClientRect()o valor da propriedade do objeto mostra 0 para o IE. Nesse caso, você deve definir display = 'block'o elemento. Você pode usar o código abaixo para que todo o navegador seja compensado.

Estenda a funcionalidade jQuery:

(function($) {
    jQuery.fn.weOffset = function () {
        var de = document.documentElement;
        $(this).css("display", "block");
        var box = $(this).get(0).getBoundingClientRect();
        var top = box.top + window.pageYOffset - de.clientTop;
        var left = box.left + window.pageXOffset - de.clientLeft;
        return { top: top, left: left };
    };
}(jQuery));

Usar :

var elementOffset = $("#" + elementId).weOffset();
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.