如何获取元素相对于浏览器视口的顶部位置?

265

我想要获取一个元素相对于浏览器视口(即页面显示的视口,而不是整个页面)的位置。如何使用JavaScript实现?

非常感谢


2
我认为这个问题类似于https://dev59.com/43VC5IYBdhLWcg3wsTVi,该问题有一个非常好的解决方案。 - Jakob Runge
3
@JakobRunge,那在2013年是“不错”的意思? - Iulian Onofrei
3
考虑接受一个解决方案。 - Iulian Onofrei
13个回答

410
现有的答案已经过时了。本地getBoundingClientRect()方法已经存在了一段时间,并且正好可以满足问题的要求。此外,它支持所有浏览器(包括IE 5,似乎!)
来自MDN页面
返回的值是一个TextRectangle对象,其中包含只读的left、top、right和bottom属性,描述了边框框,以像素为单位,其左上角相对于视口的左上角。
你可以这样使用它:
var viewportOffset = el.getBoundingClientRect();
// these are relative to the viewport, i.e. the window
var top = viewportOffset.top;
var left = viewportOffset.left;

4
在使用浏览器缩放(Android Chrome)时,可能会出现问题。 @rism提供的解决方案(见下文)适用于这种情况。 - Dmitry
7
大多数情况下没问题,不过这假设该元素不在嵌套的 iframe 中,是吗?问题是关于相对于浏览器窗口的位置。el.getBoundingClientRect() 会返回相对于 iframe 窗口而非浏览器窗口的位置。 - cakidnyc
16
这个问题是关于浏览器窗口大小的相对性。 - Dmitry Dvornikov
3
@Denilson getBoundingClientRect().top在我的IE11浏览器中返回0。你有什么解决办法吗? - Arif
@Arif:抱歉,我无法重现您的问题。我在IE11上进行了测试,仍然得到了结果:https://codepen.io/denilsonsa/pen/ypqyXj - Denilson Sá Maia
2
你可以用 const { top, left } = viewportOffset 简化它。 - David Ferreira

111

为了确保滚动条的正常工作,我在代码中添加了window.scroll:

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

这使我能够获得元素在文档上的真实相对位置,即使它已经滚动。


3
你应该加上滚动位置而不是减去它,对吗? - Iulian Onofrei
我已经点赞了这个回答,但是@lulian Onofrei说得对。请编辑一下。 - pmrotule
1
@Fabio getBoundingClientRect().top 在我的IE11中返回0,你有什么解决方案吗? - Arif
4
window.scrollY || window.pageYOffset 可能会改善支持。 - Fabian von Ellerts
太棒了!这正是我需要的。 - idiglove
显示剩余2条评论

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

1
这似乎是从文档顶部计算,而不是视口。 - Scott L

19

编辑:添加一些代码以考虑页面滚动。

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);
    }
}

id参数是您想要获取偏移量的元素的ID。改编自quirksmode文章


2
谢谢您的回答,但我认为您误解了我的问题,我知道如何获取元素相对于页面顶部的位置,但我想要做的是获取相对于“视口”的位置(即浏览器窗口而不是文档)。 - Waleed Eissa
抱歉,您使用的“viewport”让我感到困惑。所以,您是在说您想要考虑浏览器的工具栏、书签工具栏、地址栏等吗? - Derek Swingley
不,它只是你看到页面的窗口。如果页面被滚动,这将与文档顶部不同,否则它们是相同的。 - Waleed Eissa
哦,好的,我明白了。不太确定该怎么做...如果我想到了什么,我会编辑我的答案。 - Derek Swingley
我把上面的代码变得有点丑陋,但它能够工作...使用offsetParent.scrollTop。 - Derek Swingley
非常感谢。我看到原型使用parentNode而不是offsetParent来获取scrollTop,但我不知道原因。 - Waleed Eissa

13

jQuery非常优雅地实现了这一点。如果您查看jQuery的offset源代码,您会发现它基本上是这样实现的:

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

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

9
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;
}

source


5
您可以尝试以下方法:node.offsetTop - window.scrollY。在定义了视口元标记的 Opera 上可以使用该方法。

12
如果我理解文档正确的话,offsetTop相对于最近的已定位元素。这可能是根元素,也可能不是,具体取决于页面结构。 - Denilson Sá Maia

5

这个页面上的函数将返回一个矩形,其中包括传递元素相对于浏览器视口的顶部、左侧、高度和宽度坐标。

    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;
}

3

假设网页中存在一个ID为btn1的元素,并且已经引入了jQuery。这在Chrome、FireFox、IE >=9和Edge等所有现代浏览器中都可以工作。 jQuery只用于确定相对于文档的位置。

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。我相信您的意思是“它并不是100%原生的纯JS”。 - hungerstar

2
有时在IE浏览器中,getBoundingClientRect()对象的属性值会显示为0。这种情况下,您需要为该元素设置display = 'block'。以下代码可用于所有浏览器以获取偏移量。
扩展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));

使用:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接