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.