¿Cómo obtener la posición superior de un elemento en relación con la ventana gráfica del navegador?

173

Quiero obtener la posición de un elemento en relación con la ventana gráfica del navegador (la ventana gráfica en la que se muestra la página, no toda la página). ¿Cómo se puede hacer esto en JavaScript?

Muchas gracias

Waleed Eissa
fuente
2
Creo que esta pregunta es similar a stackoverflow.com/questions/211703/... que tiene una solución muy buena.
Jakob Runge
1
@JakobRunge, ¿Eso fue "agradable" en 2013?
Iulian Onofrei
1
Considera aceptar una solución.
Iulian Onofrei

Respuestas:

306

Las respuestas existentes ahora están desactualizadas. El getBoundingClientRect()método nativo ha existido desde hace bastante tiempo y hace exactamente lo que pide la pregunta. Además, es compatible con todos los navegadores (¡al parecer, IE 5!)

Desde la página de MDN :

El valor devuelto es un objeto TextRectangle, que contiene propiedades de solo lectura izquierda, superior, derecha e inferior que describen el cuadro de borde, en píxeles, con la parte superior izquierda relativa a la parte superior izquierda de la ventana gráfica .

Lo usas así:

var viewportOffset = el.getBoundingClientRect();
// these are relative to the viewport, i.e. the window
var top = viewportOffset.top;
var left = viewportOffset.left;
Himanshu P
fuente
3
Es posible que esto no funcione correctamente cuando use el zoom del navegador (Android Chrome). La solución de @rism ( ver abajo ) funciona en este caso
Dmitry
44
Bueno para la mayoría de los casos, pero supone que el elemento no está dentro de un iframe incrustado, ¿verdad? La pregunta es acerca de la ventana relativa del navegador. El.getBoundingClientRect () devolverá la ventana del iframe, no la ventana del navegador.
cakidnyc
66
La pregunta es acerca de la ventana relativa del navegador.
Dmitry Dvornikov
2
@Denilson getBoundingClientRect().topno está en mi IE11, devuelve 0. ¿Tiene alguna solución?
Arif
@Arif: Lo siento, pero no puedo reproducir tu problema. Probé
Denilson Sá Maia
57

En mi caso, solo para estar seguro con respecto al desplazamiento, agregué el window.scroll a la ecuación:

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

Eso me permite obtener la posición relativa real del elemento en el documento, incluso si se ha desplazado.

Fabio Nolasco
fuente
2
¿No se supone que debes agregar la posición de desplazamiento en lugar de restarla?
Iulian Onofrei
Elevé la respuesta, pero aún así, @lulian Onofrei tiene razón. Por favor editar
pmrotule
@Fabio getBoundingClientRect().topno está en mi IE11, devuelve 0. ¿Tiene alguna solución?
Arif
1
@Arif window.scrollY || window.pageYOffsetpuede mejorar el soporte
Fabian von Ellerts
@FabianvonEllerts :)
Arif
15

Editar: agregue un código para tener en cuenta el desplazamiento de la 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);
    }
}

El argumento id es el id del elemento cuyo desplazamiento desea. Adaptado de una publicación peculiar .

Derek Swingley
fuente
1
Gracias por su respuesta, pero creo que entendió mal mi pregunta, sé cómo obtener la parte superior de un elemento en relación con la página, lo que estoy tratando de hacer es obtener la posición relativa a la 'ventana gráfica' (es decir, la ventana del navegador , no el documento)
Waleed Eissa
Lo siento, tu uso de 'viewport' me arrojó. Entonces, ¿estás diciendo que quieres dar cuenta del navegador Chrome, es decir, la barra de herramientas de marcadores, la barra de ubicación, etc.?
Derek Swingley
No, solo la ventana a través de la cual ves la página. Si se desplaza la página, será diferente a la parte superior del documento; de lo contrario, son iguales.
Waleed Eissa
Oh está bien entiendo. No estoy seguro de cómo hacerlo ... editaré mi respuesta si se me ocurre algo.
Derek Swingley
Hice el código anterior un poco más feo pero funciona ... use offsetParent.scrollTop
Derek Swingley
10
var element =  document.querySelector('selector');
var bodyRect = document.body.getBoundingClientRect(),
    elemRect = element.getBoundingClientRect(),
    offset   = elemRect.top - bodyRect.top;
GURU PRASAD
fuente
Esto parece calcularse desde la parte superior del documento, no desde la ventana gráfica
Scott Leonard el
6

Puedes probar:

node.offsetTop - window.scrollY

Funciona en Opera con la metaetiqueta viewport definida.

Szere Dyeri
fuente
77
Si entiendo la documentación correctamente, offsetTopes relativa al elemento posicionado más cercano. Que, dependiendo de la estructura de la página, puede ser o no un elemento raíz.
Denilson Sá Maia
5

jQuery implementa esto con bastante elegancia. Si nos fijamos en la fuente de jQuery offset, encontrará que esto es básicamente cómo se implementa:

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

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

La función en esta página devolverá un rectángulo con las coordenadas superior, izquierda, altura y anchura de un elemento pasado en relación con el puerto de visualización del 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;
}
rismo
fuente
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;
}

fuente

karlsebal
fuente
1

Gracias por todas las respuestas. Parece que Prototype ya tiene una función que hace esto (la función page ()). Al ver el código fuente de la función, descubrí que primero calcula la posición de desplazamiento del elemento en relación con la página (es decir, la parte superior del documento), luego resta el scrollTop de eso. Vea el código fuente del prototipo para más detalles.

Waleed Eissa
fuente
Buena decisión. Probablemente sea mejor ir con una de las bibliotecas principales, ya que es más probable que den el mismo resultado en todos los navegadores. Si tienes curiosidad, mira mi respuesta. El código allí hará esto, pero no tengo idea de cómo se mantendrá en todos los navegadores.
Derek Swingley
1

Supongo que un elemento que tiene una identificación de btn1existe en la página web, y también que jQuery está incluido. Esto ha funcionado en todos los navegadores modernos de Chrome, Firefox, IE> = 9 y Edge. jQuery solo se usa para determinar la posición relativa al 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
fuente
1
jQuery es JavaScript. Creo que querías decir "no es 100% vainilla / JS puro".
hungerstar
Sí, a eso me refería.
Sunil
1

A veces, getBoundingClientRect()el valor de la propiedad del objeto muestra 0 para IE. En ese caso, debe configurar display = 'block'el elemento. Puede usar el siguiente código para que todos los navegadores se compensen.

Extienda la funcionalidad de 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));

Utilizar :

var elementOffset = $("#" + elementId).weOffset();
Arif
fuente