我想知道如何在 JavaScript 中获取 img
和 div
等 HTML 元素的 X 和 Y 位置。
正确的做法是使用 element.getBoundingClientRect()
:
var rect = element.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);
只要您可能关心它,Internet Explorer 就一直支持这一点,并且最终在 CSSOM Views 中实现了标准化。所有其他浏览器都采用它a long time ago。
一些浏览器还返回高度和宽度属性,尽管这是非标准的。如果您担心较旧的浏览器兼容性,请检查此答案的修订以获取优化的降级实施。
element.getBoundingClientRect()
返回的值是相对于视口的。如果您需要它相对于另一个元素,只需从另一个矩形中减去一个矩形:
var bodyRect = document.body.getBoundingClientRect(),
elemRect = element.getBoundingClientRect(),
offset = elemRect.top - bodyRect.top;
alert('Element is ' + offset + ' vertical pixels from <body>');
此函数返回元素相对于整个文档(页面)的位置:
function getOffset(el) {
const rect = el.getBoundingClientRect();
return {
left: rect.left + window.scrollX,
top: rect.top + window.scrollY
};
}
使用它我们可以得到 X 位置:
getOffset(element).left
...或Y位置:
getOffset(element).top
库会竭尽全力获得元素的准确偏移量。这是一个简单的函数,可以在我尝试过的每种情况下完成这项工作。
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;
如果您只想在 javascript 中完成,这里有一些使用 getBoundingClientRect()
的 one liner
window.scrollY + document.querySelector('#elementId').getBoundingClientRect().top // Y
window.scrollX + document.querySelector('#elementId').getBoundingClientRect().left // X
第一行将返回 offsetTop
说 Y 相对于文档。第二行将返回 offsetLeft
说 X 相对于文档。
getBoundingClientRect()
是一个 javascript 函数,它返回元素相对于窗口视口的位置。
大多数浏览器上的 HTML 元素将具有:-
offsetLeft
offsetTop
这些指定元素相对于其最近的具有布局的父元素的位置。如果有 offsetParent 属性,通常可以访问此父级。
IE和FF3都有
clientLeft
clientTop
这些属性不太常见,它们使用其父客户区域指定元素位置(填充区域是客户区域的一部分,但边框和边距不是)。
如果页面包含 - 至少 - 任何“DIV”,meouw 给出的函数会抛出超出当前页面限制的“Y”值。为了找到准确的位置,您需要同时处理 offsetParent 和 parentNode。
尝试下面给出的代码(检查 FF2):
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];
};
您可以向 Element.prototype
添加两个属性以获取任何元素的顶部/左侧。
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 );
}
} );
这被称为:
var x = document.getElementById( 'myDiv' ).documentOffsetLeft;
这是一个将结果与 jQuery 的 offset().top
和 .left
进行比较的演示:http://jsfiddle.net/ThinkingStiff/3G7EZ/
Element.prototype
通常被认为是一个坏主意。这导致代码很难维护。此外,此代码不考虑滚动。
有效地检索相对于页面的位置,而不使用递归函数:(也包括 IE)
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,它会返回left或top,我们也可以将它们组合起来:
1) 找到左边
function findLeft(element) {
var rec = document.getElementById(element).getBoundingClientRect();
return rec.left + window.scrollX;
} //call it like findLeft('#header');
2)找到顶部
function findTop(element) {
var rec = document.getElementById(element).getBoundingClientRect();
return rec.top + window.scrollY;
} //call it like findTop('#header');
或 3) 一起找到 left 和 top
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');
jQuery .offset() 将获取第一个元素的当前坐标,或者设置匹配元素集中每个元素相对于文档的坐标。
这是一个使用 vanilla JS 递归迭代 element.offsetTop
和 element.offsetParent
的现代 1-liner:
功能:
getTop = el => el.offsetTop + (el.offsetParent && getTop(el.offsetParent))
用法:
const el = document.querySelector('#div_id');
const elTop = getTop(el)
优势:
无论当前滚动位置如何,始终返回绝对垂直偏移量。
传统语法:
function getTop(el) {
return el.offsetTop + (el.offsetParent && getTop(el.offsetParent));
}
使用 JavaScript 框架可能会更好地为您服务,该框架具有以独立于浏览器的方式返回此类信息(以及更多!)的功能。这里有几个:
原型
jQuery
MooTools
YUI(雅虎)
使用这些框架,您可以执行以下操作:$('id-of-img').top
获取图像的 y 像素坐标。
更新:
递归方法(在我的旧答案中)创建了许多调用堆栈。在这种情况下,我们可以使用 while 循环来避免递归:
/**
*
* @param {HTMLElement} el
* @return {{top: number, left: number}}
*/
function getDocumentOffsetPosition(el) {
let top = 0, left = 0;
while (el !== null) {
top += el.offsetTop;
left += el.offsetLeft;
el = el.offsetParent;
}
return {top, left};
}
老答案:
/**
*
* @param {HTMLElement} el
* @return {{top: number, left: number}}
*/
function getDocumentOffsetPosition(el) {
var position = {
top: el.offsetTop,
left: el.offsetLeft
};
if (el.offsetParent) {
var parentPosition = getDocumentOffsetPosition(el.offsetParent);
position.top += parentPosition.top;
position.left += parentPosition.left;
}
return position;
}
感谢ThinkingStiff的the answer,这只是另一个版本。
我接受了@meouw 的回答,添加到允许边框的clientLeft 中,然后创建了三个版本:
getAbsoluteOffsetFromBody - 类似于@meouw,它获取相对于文档正文或 html 元素的绝对位置(取决于 quirks 模式)
getAbsoluteOffsetFromGivenElement - 返回相对于给定元素 (relativeEl) 的绝对位置。请注意,给定元素必须包含元素 el,否则其行为与 getAbsoluteOffsetFromBody 相同。如果您有两个元素包含在另一个(已知)元素中(可选地,节点树上的几个节点)并希望使它们处于相同位置,这将很有用。
getAbsoluteOffsetFromRelative - 返回相对于第一个具有 position: relative 的父元素的绝对位置。这类似于 getAbsoluteOffsetFromGivenElement,出于同样的原因,但只会到达第一个匹配元素。
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.greywyvern.com/?post=331 - 我注意到 getStyle 中至少有一段有问题的代码,假设浏览器运行良好,但根本没有测试其余代码。
小与小的区别
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
<svg>
元素没有 offsetLeft
、offsetTop
和 offsetParent
属性。
DIV
或 IMG
,而不是 SVG
。查看“查看示例坐标”?你可以发现对象 svg 是位置调用 getOffsetRect,尝试依赖对象工作。
如果您使用的是 jQuery,这可能是一个简单的解决方案:
<script>
var el = $("#element");
var position = el.position();
console.log( "left: " + position.left + ", top: " + position.top );
</script>
如果使用 jQuery,dimensions plugin 非常好,可以让您准确指定您想要的内容。
例如
相对位置、绝对位置、不带填充的绝对位置、带填充...
它继续,让我们说你可以用它做很多事情。
加上使用 jQuery 的好处是它的文件大小轻且易于使用,之后没有它你就不会回到 JavaScript。
要获得元素的总偏移量,您可以递归地总结所有父偏移量:
function getParentOffset(el): number {
if (el.offsetParent) {
return el.offsetParent.offsetTop + getParentOffset(el.offsetParent);
} else {
return 0;
}
}
使用此实用函数,dom 元素的总顶部偏移量为:
el.offsetTop + getParentOffset(el);
我发现的最简洁的方法是 jQuery offset
使用的技术的简化版本。与其他一些答案类似,它以 getBoundingClientRect
开头;然后它使用 window
和 documentElement
来调整滚动位置以及 body
上的边距(通常是默认值)。
var rect = el.getBoundingClientRect();
var docEl = document.documentElement;
var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;
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 + ""; } div { 宽度:100px;高度:100px;背景颜色:红色;边框:1px纯黑色; } #rel { 位置:相对;左:10px;顶部:10px; } #abs { 位置:绝对;顶部:250 像素;左:250px; }
这是我设法创建的最好的代码(也适用于 iframe,与 jQuery 的 offset() 不同)。似乎 webkit 有一点不同的行为。
根据 meouw 的评论:
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 };
}
虽然这很可能在这么多答案的底部丢失,但这里的顶级解决方案对我不起作用。据我所知,其他任何答案都没有帮助。情况:在 HTML5 页面中,我有一个菜单,它是标题内的导航元素(不是标题,而是另一个元素中的标题)。一旦用户滚动到它,我希望导航保持在顶部,但在此之前,标题是绝对定位的(所以我可以让它稍微覆盖其他东西)。上面的解决方案从未触发更改,因为 .offsetTop 不会更改,因为这是一个绝对定位的元素。此外,.scrollTop 属性只是最顶部元素的顶部......也就是说 0 并且始终为 0。我使用这两个执行的任何测试(与 getBoundingClientRect 结果相同)都不会告诉我顶部是否导航栏曾经滚动到可查看页面的顶部(同样,正如控制台中所报告的,它们在滚动发生时只是保持相同的数字)。
解决方案我的解决方案是利用
window.visualViewport.pageTop
pageTop 属性的值反映了屏幕的可视部分,因此允许我跟踪元素相对于可视区域边界的位置。
可能没必要说,每当我处理滚动时,我都希望使用此解决方案以编程方式响应被滚动元素的移动。
希望对其他人有所帮助。
重要提示:This appears to work in Chrome and Opera currently & definitely not in Firefox (6-2018)...在 Firefox 支持 visualViewport 之前,我建议不要使用此方法,(我希望他们很快就会这样做...它比其他方法更有意义)。
更新:
关于此解决方案的说明。
虽然我仍然发现我发现对于“...以编程方式响应正在滚动的元素的移动”的情况非常有价值。适用。我遇到的问题的更好解决方案是使用 CSS 在元素上设置 position: sticky。使用粘性,您可以在不使用 javascript 的情况下让元素保持在顶部(注意:有时这不会像将元素更改为固定那样有效,但对于大多数用途而言,粘性方法可能会更好)
UPDATE01:
所以我意识到,对于不同的页面,我需要在一个稍微复杂的滚动设置中检测元素的位置(视差加上作为滚动过去的元素的一部分)一个消息)。在那种情况下,我意识到以下提供了我用来确定何时做某事的价值:
let bodyElement = document.getElementsByTagName('body')[0];
let elementToTrack = bodyElement.querySelector('.trackme');
trackedObjPos = elementToTrack.getBoundingClientRect().top;
if(trackedObjPos > 264)
{
bodyElement.style.cssText = '';
}
希望这个答案现在更广泛有用。
HTML 程序通过将鼠标拖到元素上来显示 (x, y) 您刚刚复制它并在您自己的位置上使用它
将鼠标移到文本上
我这样做是为了与旧浏览器交叉兼容。
// 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
我成功地使用了 Andy E 的解决方案来定位引导程序 2 模式,具体取决于用户单击表格行中的链接。该页面是 Tapestry 5 页面,下面的 javascript 被导入到 java 页面类中。
javascript:
function setLinkPosition(clientId){
var bodyRect = document.body.getBoundingClientRect(),
elemRect = clientId.getBoundingClientRect(),
offset = elemRect.top - bodyRect.top;
offset = offset + 20;
$('#serviceLineModal').css("top", offset);
}
我的模态代码:
<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%;">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
<h3 id="myModalLabel">Modal header</h3>
</div>
<div class="modal-body">
<t:zone t:id="modalZone" id="modalZone">
<p>You selected service line number: ${serviceLineNumberSelected}</p>
</t:zone>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
<!-- <button class="btn btn-primary">Save changes</button> -->
</div>
循环中的链接:
<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"></i> <!-- ${service.serviceLineNumber} -->
</a>
</td>
以及页面类中的java代码:
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');");
}
});
}
希望这对某人有所帮助,这篇文章有所帮助。
经过大量研究和测试,这似乎有效
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/xuvovalifo/edit?html,js,output
只是想我也会把这个扔出去。我无法在较旧的浏览器中对其进行测试,但它适用于最新的前 3 名浏览器。:)
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()};
};
由于不同的浏览器以不同的方式呈现边框、填充、边距等。我写了一个小函数来检索您想要精确尺寸的每个根元素中特定元素的顶部和左侧位置:
function getTop(root, offset) {
var rootRect = root.getBoundingClientRect();
var offsetRect = offset.getBoundingClientRect();
return offsetRect.top - rootRect.top;
}
要检索左侧位置,您必须返回:
return offsetRect.left - rootRect.left;
这很简单,就像 JS 中的两行代码一样:
var elem = document.getElementById("id");
alert(elem.getBoundingClientRect());
获取 div 相对于 left 和 Top 的位置
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
el.offsetTop
和 el.offsetLeft
(OP 没有说任何关于 jQuery 的内容......我不确定你的答案为什么也使用 jQuery......)
BoundingClientRect
在滚动时发生变化