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

Waleed Eissa
fonte
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;
Himanshu P
fonte
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.

Fabio Nolasco
fonte
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 .

Derek Swingley
fonte
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;
GURU PRASAD
fonte
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.

Szere Dyeri
fonte
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
};
jordancooperman
fonte
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;
}
arriscar
fonte
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

karlsebal
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.

Waleed Eissa
fonte
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);
Sunil
fonte
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();
Arif
fonte