CoreyJia 2017-03-14
文档坐标和视口坐标
视口坐标是相对于窗口的坐标,而文档坐标是相对于整个文档而言。例如,在文档坐标中如果一个元素的相对于文档的Y坐标是200px,并且用户已经把浏览器向下滚动了75px,那么视口坐标中元素的Y坐标为200px–75px=125px。
如何获取浏览器滚动条的位置?Window对象的pageXOffset和pageYOffset属性在所有浏览器中提供这些值,除IE8以及更早的版本。IE和所有现代浏览器也可以通过scrollLeft和scrollTop属性获取滚动条位置。
下面代码的getScrollOffsets方法获取滚动条位置:
//以一个对象的x和y属性放回滚动条的位置
functiongetScrollOffsets(w){
w=w||window;
//除了IE8以及更早的版本以外,其他浏览器都支持
if(w.pageXOffset!=null)return{x:w.pageXOffset,y:w.pageYOffset};
//对标准模式下的IE
vard=w.document;
if(document.compatMode=="CSS1Compat")
return{x:d.documentElement.scrollLeft,y:d.documentElement.scrollTop};
//对怪异模式下的浏览器
return{x:d.body.scrollLeft,y:d.body.scrollTop};
}
复制代码
有时候能够判定视口的尺寸也是非常有用的,下面的代码简便地查询视口尺寸:
//作为一个对象的w和h属性返回视口的尺寸
functiongetViewportSize(w){
//使用指定的窗口,如果不带参数则使用当前窗口
w=w||window;
//除了IE8及更早的版本以外,其他浏览器都能用
if(w.innerWidth!=null)
return{w:w.innerWidth,h:w.innerHeight};
//对标准模式下的IE(或任意浏览器)
vard=w.document;
if(document.compatMode=="CSS1Compat")
return{w:d.documentElement.clientWidth,h:d.documentElement.clientHeight};
//对怪异模式下的浏览器
return{w:d.body.clientWidth,h:d.body.clientHeight};
}
复制代码
上面的两个例子已经使用到scrollLeft、scrollTop、clientWidth、clientHeight。scrollLeft和scrollTop获取滚动条位置,而clientWidth和clientHeight获取对象的尺寸。
查询元素的几何尺寸
判定一个元素的尺寸和位置最简单的方法是调用它的getBoundingClientRect()方法。该方法是在IE5中引入的,而现在当前的所有浏览器都实现了。它不需要参数,返回left、right、top、bottom属性的对象。
这个方法返回元素在视口坐标中的位置。为了转换为甚至用户滚动浏览器窗口以后任然有效的文档坐标,需要加上滚动偏移量:
//元素相对于文档的坐标位置
functiongetElementRect(e){
varbox=e.getBoundingClientRect();
varoffsets=getScrollOffsets();
varx=box.left+offsets.x;
vary=box.top+offsets.y;
return{x:x,y:y};
}
复制代码
在很多浏览器中,getBoundingClientRect()返回的对象还包括width和height属性。但在原始的IE中未实现。可以这样计算元素的width和height:
//元素尺寸
functiongetElementSize(e){
varbox=getElementRect(e);
varw=box.width||box.right-box.left;
varh=box.height||box.bottom-box.top;
return{w:w,h:h};
}
复制代码
滚动元素
之前的getScrollOffsets方法可以查询滚动条的位置。该例子的scrollLeft和scrollTop属性可以用来设置让浏览器滚动,但有一种更简单的方法从Javascript最早时期开始支持的。Window对象的scrollTop()方法接口一个点的X和Y坐标(文档坐标),并作为滚动条的偏移量设置它们。下面代码滚动浏览器到文档最下面的页面可见:
//滚动到浏览器最底部
functionscrollToBottom(){
//获取文档和视口的高度
vardocumentHeight=document.documentElement.offsetHeight;
varviewportHeight=window.innerHeight;//或使用上面的getViewPortSize()方法
//然后,滚动让最后一页在视口中可见
window.scrollTo(0,documentHeight-viewportHeight);
}
复制代码
Window的scrollBy方法和scroll()和scrollTo()类似,但是它的参数是相对的,并在当前滚动条的偏移量上增加。例如,快速阅读者可能会喜欢这样:
javascript:voidsetInterval(function(){scrollBy(0,10)},200);
复制代码
如果想让某个元素在文档中可见,可以利用getBoundingClientRect()计算元素的位置,并转换为文档坐标,然后使用scrollTo()方法达到目的。但在需要显示Html元素上调用scrollIntoView()方法更方便。
scrollIntoView()的行为与设置window.location.hash为一个命名锚点的名字后浏览器产生的行为类似。
元素尺寸、位置和溢出
任何HTML元素的只读属性offsetWidth和offsetHeight以CSS像素返回它的屏幕尺寸。返回的尺寸包含元素的边框和内边距,除去了外边距。
所有HTML元素拥有offsetLeft和offsetTop属性来返回元素的X和Y坐标。这些值是文档坐标,并直接指定元素的位置。当对于已定位元素的后代元素和一些其他元素,这些属性返回的坐标是相对于祖先元素的而非文档。
offsetParent属性指定这些属性所相对的父元素。如果offsetParent为null,这些属性都是文档坐标,因此,一般来说,用offsetLeft和offsetTop来计算元素e的位置需要一个循环:
//计算元素位置
functiongetElementPosition(e){
varx=0,y=0;
while(e!=null){
x+=e.offsetLeft;
y+=e.offsetTop;
e=e.offsetParent;
}
return{x:x,y:y};
}
复制代码
getElementPosition函数也不总是计算正确的值,下面看如何修复它。除了这些名字以offset开头的属性以外,所有的文档元素定义了其他两组属性,名字一组以client开头,另一组以scroll开头。即每个元素都有以下这些属性:
我问问.png
为了理解client和scroll属性,你需要知道元素的实际内容可能比分配用来容纳的盒子更大,因此单个元素可能有滚动条。内容区域是视口,就像浏览器窗口,当实际内容比视口大,需要把元素滚动套位置考虑进去。
clientWidth和clientHeight类似offsetWidth和offsetHeight,区别在于它们不包含边框大小。只包含内容和内边距。同时,如果浏览器在内边距和边框之间添加了滚动条,clientWidth和clientHeight不包含滚动条尺寸。在文档的根元素上查询这些属性时,它们的返回值和窗口的innerWidth和innerHeight属性值相等。
clientLeft和clientTop属性没什么用:它们返回元素的内边距的外边框和它的边框的外边缘之间的水平距离和垂直距离。
scrollWidth和scrollHeight是元素的内容区域加上它的内边距再加上任何溢出内容的尺寸。当内容正好和内容区域匹配没溢出时,这些属性与clientWidth和clientHeight相等。有溢出时,包含了溢出的内容尺寸。
scollLeft和scrollTop指定元素滚动条的位置。在getScrollOffsets()方法中查询过它们。注意,scrollLeft和scrollTop是可写的,通过设置它们来让元素中的内容滚动(HTML元素并没有类似Window对象的scrollTo()方法。
DEMO
下面代码介绍了前面几个函数的使用:
<!DOCTYPEhtml>
<htmllang="en">
<head>
<metacharset="UTF-8">
<title>Title</title>
<scripttype="text/javascript">
//以一个对象的x和y属性放回滚动条的位置
functiongetScrollOffsets(w){
w=w||window;
//除了IE8以及更早的版本以外,其他浏览器都支持
if(w.pageXOffset!=null)return{x:w.pageXOffset,y:w.pageYOffset};
//对标准模式下的IE
vard=w.document;
if(document.compatMode=="CSS1Compat")
return{x:d.documentElement.scrollLeft,y:d.documentElement.scrollTop};
//对冠以模式下的浏览器
return{x:d.body.scrollLeft,y:d.body.scrollTop};
}
//作为一个对象的w和h属性返回视口的尺寸
functiongetViewportSize(w){
//使用指定的窗口,如果不带参数则使用当前窗口
w=w||window;
//除了IE8及更早的版本以外,其他浏览器都能用
if(w.innerWidth!=null)
return{w:w.innerWidth,h:w.innerHeight};
//对标准模式下的IE(或任意浏览器)
vard=w.document;
if(document.compatMode=="CSS1Compat")
return{w:d.documentElement.clientWidth,h:d.documentElement.clientHeight};
//对怪异模式下的浏览器
return{w:d.body.clientWidth,h:d.body.clientHeight};
}
//元素相对于文档的坐标位置
functiongetElementRect(e){
varbox=e.getBoundingClientRect();
varoffsets=getScrollOffsets();
varx=box.left+offsets.x;
vary=box.top+offsets.y;
return{x:x,y:y};
}
//元素尺寸
functiongetElementSize(e){
varbox=getElementRect(e);
varw=box.width||box.right-box.left;
varh=box.height||box.bottom-box.top;
return{w:w,h:h};
}
//滚动到浏览器最底部
functionscrollToBottom(){
//获取文档和视口的高度
vardocumentHeight=document.documentElement.offsetHeight;
varviewportHeight=window.innerHeight;//或使用上面的getViewPortSize()方法
//然后,滚动让最后一页在视口中可见
window.scrollTo(0,documentHeight-viewportHeight);
}
//计算元素位置
functiongetElementPosition(e){
varx=0,y=0;
while(e!=null){
x+=e.offsetLeft;
y+=e.offsetTop;
e=e.offsetParent;
}
return{x:x,y:y};
}
</script>
</head>
<body>
<buttonid="scrolltoBottomBtn">滚动到浏览器</button>
<divstyle="height:400px;background:red;">
</div>
<buttonid="btn">获取滚动条位置</button>
<buttonid="viewportBtn">获取视口尺寸</button>
<buttonid="eleRectBtn">元素文档坐标</button>
<scripttype="text/javascript">
varbtn=document.getElementById("btn");
btn.onclick=function(event){
console.log(getScrollOffsets());
}
varviewportBtn=document.getElementById("viewportBtn");
viewportBtn.onclick=function(event){
console.log(getViewportSize());
}
vareleRectBtn=document.getElementById("eleRectBtn");
eleRectBtn.onclick=function(eevent){
console.log(getElementRect(this));
}
varscrolltoBottomBtn=document.getElementById("scrolltoBottomBtn");
scrolltoBottomBtn.onclick=function(){
scrollToBottom();
}
</script>
</body>
</html>
转自解放号社区:http://bbs.jointforce.com/topic/26095