关于javascript:检索HTML元素的位置(X,Y)

Retrieve the position (X,Y) of an HTML element

我想知道如何在javascript中获取HTML元素(如imgdiv的x和y位置。


正确的方法是使用element.getBoundingClientRect()

1
2
var rect = element.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);

Internet Explorer已经支持这一点,只要您可能关心它,并且它最终在CSSOM视图中被标准化。其他所有的浏览器很久以前就采用了它。

有些浏览器还返回高度和宽度属性,尽管这是非标准的。如果您担心旧版浏览器的兼容性,请检查此答案的修订版以获得优化的降级实现。

element.getBoundingClientRect()返回的值是相对于视区的。如果需要相对于另一个元素,只需从另一个元素中减去一个矩形:

1
2
3
4
5
var bodyRect = document.body.getBoundingClientRect(),
    elemRect = element.getBoundingClientRect(),
    offset   = elemRect.top - bodyRect.top;

alert('Element is ' + offset + ' vertical pixels from <body>');


库将使用一定的长度来获取元素的精确偏移量。这是一个简单的函数,它可以在我尝试过的所有情况下完成工作。

1
2
3
4
5
6
7
8
9
10
11
function getOffset( el ) {
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
        _x += el.offsetLeft - el.scrollLeft;
        _y += el.offsetTop - el.scrollTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}
var x = getOffset( document.getElementById('yourElId') ).left;


这对我很有效(根据最高投票结果修改):

1
2
3
4
5
6
7
function getOffset(el) {
  const rect = el.getBoundingClientRect();
  return {
    left: rect.left + window.scrollX,
    top: rect.top + window.scrollY
  };
}

用这个我们可以打电话

1
getOffset(element).left

1
getOffset(element).top


如果page包含-至少-任何"div",meouw给出的函数将"y"值抛出当前页面限制之外。为了找到准确的位置,需要同时处理offsetparent和parentnode。

尝试下面给出的代码(它检查了ff2):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var getAbsPosition = function(el){
    var el2 = el;
    var curtop = 0;
    var curleft = 0;
    if (document.getElementById || document.all) {
        do  {
            curleft += el.offsetLeft-el.scrollLeft;
            curtop += el.offsetTop-el.scrollTop;
            el = el.offsetParent;
            el2 = el2.parentNode;
            while (el2 != el) {
                curleft -= el2.scrollLeft;
                curtop -= el2.scrollTop;
                el2 = el2.parentNode;
            }
        } while (el.offsetParent);

    } else if (document.layers) {
        curtop += el.y;
        curleft += el.x;
    }
    return [curtop, curleft];
};


大多数浏览器上的HTML元素都有:

1
2
offsetLeft
offsetTop

它们指定元素相对于具有布局的最近父元素的位置。通常可以通过offsetparent属性访问此父级。

IE和FF3

1
2
clientLeft
clientTop

这些属性不太常见,它们用其父客户端区域指定元素位置(填充区域是客户端区域的一部分,但边框和边距不是)。


您可以向Element.prototype添加两个属性,以获取任何元素的顶部/左侧。

1
2
3
4
5
6
7
8
9
10
11
Object.defineProperty( Element.prototype, 'documentOffsetTop', {
    get: function () {
        return this.offsetTop + ( this.offsetParent ? this.offsetParent.documentOffsetTop : 0 );
    }
} );

Object.defineProperty( Element.prototype, 'documentOffsetLeft', {
    get: function () {
        return this.offsetLeft + ( this.offsetParent ? this.offsetParent.documentOffsetLeft : 0 );
    }
} );

这叫做:

1
var x = document.getElementById( 'myDiv' ).documentOffsetLeft;

下面是一个将结果与jquery的offset().top.left进行比较的演示:http://jsfiddle.net/thinkingstifle/3g7ez/


在不使用递归函数的情况下,有效地检索相对于页面的位置:(也包括ie)

1
2
3
4
5
6
7
8
9
var element = document.getElementById('elementId'); //replace elementId with your element's Id.
var rect = element.getBoundingClientRect();
var elementLeft,elementTop; //x and y
var scrollTop = document.documentElement.scrollTop?
                document.documentElement.scrollTop:document.body.scrollTop;
var scrollLeft = document.documentElement.scrollLeft?                  
                 document.documentElement.scrollLeft:document.body.scrollLeft;
elementTop = rect.top+scrollTop;
elementLeft = rect.left+scrollLeft;


比如这样,通过传递元素的ID,它将返回左侧或顶部,我们也可以将它们组合起来:

1)查找左侧

1
2
3
4
function findLeft(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return rec.left + window.scrollX;
} //call it like findLeft('#header');

2)查找顶部

1
2
3
4
function findTop(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return rec.top + window.scrollY;
} //call it like findTop('#header');

或者3)找到左边和顶部在一起

1
2
3
4
function findTopLeft(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return {top: rec.top + window.scrollY, left: rec.left + window.scrollX};
} //call it like findTopLeft('#header');

如果您只想在javascript中完成,这里有一些使用getBoundingClientRect()的一行程序

1
2
3
window.scrollX + document.querySelector('#elementId').getBoundingClientRect().left // X

window.scrollY + document.querySelector('#elementId').getBoundingClientRect().top // Y

第一行将返回相对于文档的offsetLeft,即x。

第二行将返回相对于文档的offsetTopsay y。

getBoundingClientRect()是一个javascript函数,返回元素相对于窗口视区的位置。


使用一个javascript框架可能会更好地为您服务,它具有返回此类信息的函数(等等!)以浏览器独立的方式。这里有几个:

  • 原型
  • JQuery
  • 框架
  • YUI(雅虎)

使用这些框架,您可以执行如下操作:$('id-of-img').top以获取图像的Y像素坐标。


offset()将获取第一个元素的当前坐标,或者设置匹配元素集中每个元素相对于文档的坐标。


我接受了@meouw的回答,在clientleft中添加了允许边界的内容,然后创建了三个版本:

getabsoluteoffsetfrombody-类似于@meouw,它获取相对于文档的body或html元素的绝对位置(取决于quirks模式)

GetAbsoluteOffsetFromGivenElement-返回相对于给定元素的绝对位置(relativeel)。请注意,给定元素必须包含元素el,否则其行为将与getAbsoluteOffsetFromBody相同。如果您有两个元素包含在另一个(已知)元素中(可选地是节点树上的多个节点),并且希望使它们处于同一位置,那么这非常有用。

getabsoluteoffsetfromrelative-返回相对于第一个父元素的绝对位置,位置为:relative。这类似于GetAbsoluteOffsetFromGivenElement,原因相同,但只会到达第一个匹配元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
getAbsoluteOffsetFromBody = function( el )
{   // finds the offset of el from the body or html element
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}

getAbsoluteOffsetFromGivenElement = function( el, relativeEl )
{   // finds the offset of el from relativeEl
    var _x = 0;
    var _y = 0;
    while( el && el != relativeEl && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}

getAbsoluteOffsetFromRelative = function( el )
{   // finds the offset of el from the first parent with position: relative
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
        if (el != null)
        {
            if (getComputedStyle !== 'undefined')
                valString = getComputedStyle(el, null).getPropertyValue('position');
            else
                valString = el.currentStyle['position'];
            if (valString ==="relative")
                el = null;
        }
    }
    return { top: _y, left: _x };
}

如果您仍然有问题,特别是与滚动有关的问题,您可以尝试查看http://www.greyyvern.com/?post=331-我注意到在getStyle中至少有一段可疑的代码,假设浏览器可以正常运行,但还没有测试其他代码。


如果使用jquery,那么Dimensions插件是非常好的,它允许您精确地指定所需的内容。

例如

相对位置,绝对位置,不带填充的绝对位置,带填充…

接下来,让我们假设你可以用它做很多事情。

另外,使用jquery还有一个好处,那就是它的文件大小很轻,而且使用起来很容易,如果没有它,以后就不会返回到javascript。


这是我设法创建的最好的代码(在iframes中也可以使用,与jquery的offset()不同)。似乎webkit有点不同的行为。

基于Meouw的评论:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function getOffset( el ) {
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
        _x += el.offsetLeft - el.scrollLeft;
        _y += el.offsetTop - el.scrollTop;
        // chrome/safari
        if ($.browser.webkit) {
            el = el.parentNode;
        } else {
            // firefox/IE
            el = el.offsetParent;
        }
    }
    return { top: _y, left: _x };
}


如果使用jquery,这可能是一个简单的解决方案:

1
2
3
  var el = $("#element");
  var position = el.position();
  console.log("left:" + position.left +", top:" + position.top );

小与小的区别

1
2
3
4
5
6
7
8
9
10
function getPosition( el ) {
    var x = 0;
    var y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
    x += el.offsetLeft - el.scrollLeft;
    y += el.offsetTop - el.scrollTop;
    el = el.offsetParent;
    }
    return { top: y, left: x };
}

查看示例坐标:http://javascript.info/tutorial/coordinates


我发现最干净的方法是jquery的offset所使用的技术的简化版本。与其他一些答案类似,它从getBoundingClientRect开始;然后使用windowdocumentElement调整滚动位置以及body上的边距(通常是默认值)。

1
2
3
4
5
var rect = el.getBoundingClientRect();
var docEl = document.documentElement;

var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;

1
2
3
4
5
6
7
8
9
10
11
12
var els = document.getElementsByTagName("div");
var docEl = document.documentElement;

for (var i = 0; i < els.length; i++) {

  var rect = els[i].getBoundingClientRect();

  var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
  var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;

  els[i].innerHTML ="" + rectLeft +"," + rectTop +"";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
div {
  width: 100px;
  height: 100px;
  background-color: red;
  border: 1px solid black;
}
#rel {
  position: relative;
  left: 10px;
  top: 10px;
}
#abs {
  position: absolute;
  top: 250px;
  left: 250px;
}
1
 


我是这样做的,所以它与旧浏览器交叉兼容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// For really old browser's or incompatible ones
    function getOffsetSum(elem) {
        var top = 0,
            left = 0,
            bottom = 0,
            right = 0

         var width = elem.offsetWidth;
         var height = elem.offsetHeight;

        while (elem) {
            top += elem.offsetTop;
            left += elem.offsetLeft;
            elem = elem.offsetParent;
        }

         right = left + width;
         bottom = top + height;

        return {
            top: top,
            left: left,
            bottom: bottom,
            right: right,
        }
    }

    function getOffsetRect(elem) {
        var box = elem.getBoundingClientRect();

        var body = document.body;
        var docElem = document.documentElement;

        var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
        var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;

        var clientTop = docElem.clientTop;
        var clientLeft = docElem.clientLeft;


        var top = box.top + scrollTop - clientTop;
        var left = box.left + scrollLeft - clientLeft;
        var bottom = top + (box.bottom - box.top);
        var right = left + (box.right - box.left);

        return {
            top: Math.round(top),
            left: Math.round(left),
            bottom: Math.round(bottom),
            right: Math.round(right),
        }
    }

    function getOffset(elem) {
        if (elem) {
            if (elem.getBoundingClientRect) {
                return getOffsetRect(elem);
            } else { // old browser
                return getOffsetSum(elem);
            }
        } else
            return null;
    }

有关javascript中坐标的详细信息,请访问:http://javascript.info/tutorial/coordinates


虽然这很可能在这么多答案的底部丢失,但这里的顶级解决方案对我来说不起作用。据我所知,其他答案都没有帮助。情况:在HTML5页面中,我有一个菜单,它是一个标题中的nav元素(不是标题,而是另一个元素中的标题)。我希望导航在用户滚动到顶部时保持在顶部,但在此之前,头部是绝对定位的(所以我可以让它稍微覆盖其他内容)。上面的解决方案从未触发变化,因为offsettop不会改变,因为这是一个绝对定位元素。另外,.scrolltop属性只是最上面的元素的顶部…也就是说0总是0。我使用这两个(和getboundingclientrect结果相同)执行的任何测试都不会告诉我导航栏的顶部是否曾经滚动到可查看页面的顶部(同样,如控制台中所报告的,它们在滚动时保持相同的数字)。

解决方案我的解决方案是利用

1
window.visualViewport.pageTop

pagetop属性的值反映了屏幕的可查看部分,因此允许我跟踪元素在可查看区域边界中的位置。

可能不需要说,每当我处理滚动时,我希望使用这个解决方案对滚动的元素的移动进行编程响应。希望它能帮助别人。重要提示:这似乎在Chrome和Opera中有效,目前在Firefox中肯定不行(6-2018年)。在firefox支持visualviewport之前,我建议不要使用这种方法,(我希望他们很快能做到…)它比其他的更有意义)。更新:只是关于这个解决方案的说明。虽然我仍然发现对于"以编程方式响应滚动元素的移动"的情况非常有用。对于这个问题,更好的解决方案是使用CSS来设置位置:在元素上保持粘性。使用sticky,你可以让一个元素保持在顶部而不使用javascript(注意:有时这种方法的效果不如将元素更改为fixed有效,但大多数情况下,sticky方法可能会更好)UPDATE01所以我意识到,对于另一个页面,我有一个需求,我需要在稍微复杂的滚动设置中检测元素的位置(视差加上作为消息一部分滚动过去的元素)。我在那个场景中意识到,以下内容提供了我用来确定何时做某事的价值:

1
2
3
4
5
6
7
  let bodyElement = document.getElementsByTagName('body')[0];
  let elementToTrack = bodyElement.querySelector('.trackme');
  trackedObjPos = elementToTrack.getBoundingClientRect().top;
  if(trackedObjPos > 264)
  {
    bodyElement.style.cssText = '';
  }

希望这个答案现在能更广泛地应用。


我成功地使用了AndyE的解决方案,根据用户单击表行中的链接来定位bootstrap 2模式。页面是TopeWrice 5页面,JavaScript是在Java页面类中导入的。

JavaScript:

1
2
3
4
5
6
function setLinkPosition(clientId){
var bodyRect = document.body.getBoundingClientRect(),
elemRect = clientId.getBoundingClientRect(),
offset   = elemRect.top - bodyRect.top;
offset   = offset + 20;
$('#serviceLineModal').css("top", offset);

}

我的模态代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="serviceLineModal" class="modal hide fade add-absolute-position" data-backdrop="static"
 tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="top:50%;">

    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
    <h3 id="myModalLabel">Modal header



    <t:zone t:id="modalZone" id="modalZone">
        <p>
You selected service line number: ${serviceLineNumberSelected}
</p>
    </t:zone>



    <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
    <!-- <button class="btn btn-primary">Save changes</button> -->

循环中的链接:

1
2
3
4
5
6
7
8
9
<t:loop source="servicesToDisplay" value="service" encoder="encoder">
<tr style="border-right: 1px solid black;">      
    <td style="white-space:nowrap;" class="add-padding-left-and-right no-border">
        <a t:type="eventLink" t:event="serviceLineNumberSelected" t:context="service.serviceLineNumber"
            t:zone="pageZone" t:clientId="modalLink${service.serviceLineNumber}"
            onmouseover="setLinkPosition(this);">
            <i class="icon-chevron-down"> <!-- ${service.serviceLineNumber} -->
       
    </td>

页面类中的Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void onServiceLineNumberSelected(String number){
    checkForNullSession();
    serviceLineNumberSelected = number;
    addOpenServiceLineDialogCommand();
    ajaxResponseRenderer.addRender(modalZone);
}

protected void addOpenServiceLineDialogCommand() {
    ajaxResponseRenderer.addCallback(new JavaScriptCallback() {
        @Override
        public void run(JavaScriptSupport javascriptSupport) {
            javascriptSupport.addScript("$('#serviceLineModal').modal('show');");
        }
    });
}

希望这能帮助别人,这篇文章能帮上忙。


要获取元素的总偏移量,可以递归地求和所有父偏移量:

1
2
3
4
5
6
7
function getParentOffsets(el): number {
if (el.offsetParent) {
    return el.offsetParent.offsetTop + getParentOffset(el.offsetParent);
} else {
    return 0;
}
}

使用此实用程序函数,dom元素的总顶部偏移量为:

1
el.offsetTop + getParentOffsets(el);

After much research and testing this seems to work

1
2
3
4
5
6
7
8
9
10
function getPosition(e) {
    var isNotFirefox = (navigator.userAgent.toLowerCase().indexOf('firefox') == -1);
    var x = 0, y = 0;
    while (e) {
        x += e.offsetLeft - e.scrollLeft + (isNotFirefox ? e.clientLeft : 0);
        y += e.offsetTop - e.scrollTop + (isNotFirefox ? e.clientTop : 0);
        e = e.offsetParent;
    }
    return { x: x + window.scrollX, y: y + window.scrollY };
}

参见http://jsbin.com/xuvovalinfo/edit?HTML、JS、输出


我想我也会把这个扔出去。< BR>我还没能在老版本的浏览器中测试它,但它在前三名中的最新版本中工作。:)

1
2
3
4
5
6
7
8
9
Element.prototype.getOffsetTop = function() {
    return ( this.parentElement )? this.offsetTop + this.parentElement.getOffsetTop(): this.offsetTop;
};
Element.prototype.getOffsetLeft = function() {
    return ( this.parentElement )? this.offsetLeft + this.parentElement.getOffsetLeft(): this.offsetLeft;
};
Element.prototype.getOffset = function() {
    return {'left':this.getOffsetLeft(),'top':this.getOffsetTop()};
};

因为不同的浏览器以不同的方式呈现边框、填充、边距等。我编写了一个小函数来检索每个根元素中特定元素在精确维度中的左上方位置:

1
2
3
4
5
function getTop(root, offset) {
    var rootRect = root.getBoundingClientRect();
    var offsetRect = offset.getBoundingClientRect();
    return offsetRect.top - rootRect.top;
}

要检索左侧位置,必须返回:

1
    return offsetRect.left - rootRect.left;


Get position of div in respect to left and Top

1
2
3
  var elm = $('#div_id');  //get the div
  var posY_top = elm.offset().top;  //get the position from top
  var posX_left = elm.offset().left; //get the position from left