这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
这篇文章主要介绍了 JavaScript 位置与大小(1)——正确理解和运用与尺寸大小相关的 DOM 属性的相关资料, 需要的朋友可以参考下
在 web 开发中,不可避免遇到要计算元素大小以及位置的问题,解决这类问题的方法是利用 DOM 提供的一些 API 结合兼容性处理来,所有内容大概分 3 篇左右的文章的来说明。本文作为第一篇,介绍 DOM 提供的与尺寸大小相关的 DOM 属性,提供一些兼容性处理的方法,并结合常见的场景说明如何正确运用这些属性。
1. 正确理解 offsetWidth、clientWidth、scrollWidth 及相应的 height 属性
假设某一个元素的横纵向滚动条都拖动到最末端,则 offsetWidth、clientWidth、scrollWidth 等属性相应的范围如下图所示:
1)offsetWidth ,offsetHeight 对应的是盒模型的宽度和高度,这两个值跟我们使用 chrome 审查元素时看到的尺寸一致:
2)scrollWidth,与 scrollHeight 对应的是滚动区域的宽度和高度 , 但是不包含滚动条的宽度!滚动区域由 padding 和 content 组成。
3)clientWidth,clientHeight 对应的是盒模型除去边框后的那部分区域的宽度和高度,不包含滚动条的宽度。
4)任何一个 DOM 元素,都可以通过以下 api 快速得到 offsetWidth,clientWidth,scrollWidh 及相关的 height 属性:
//domE 为一个 DOM Html Element 对象 domE.scrollWidth domE.scrollHeight domE.clientWidth domE.clientHeight domE.offsetWidth domE.offsetHeight //domE 为一个 DOM Html Element 对象 domE.scrollWidth domE.scrollHeight domE.clientWidth domE.clientHeight domE.offsetWidth domE.offsetHeight
5) 这些属性在现代浏览器包括 pc 和 mobile 上几乎没有兼容性问题,可以放心使用 。如果你想了解详细的兼容性规则,可以参考下面的 2 篇文章:
W3C DOM Compatibility – CSS Object Model View
cssom 视图模式 cssom-view-module 相关整理与介绍
下面针对普通 html 元素,html 根元素和 body 元素的以上相关属性一一测试,以便验证前面的结论,总结一些可在实际编码过程中直接使用的经验技巧。之所以要区分普通 html 元素,html 根元素和 body 元素,是因为前面的理论,在 html 根元素和 body 元素会有一些怪异之处,需要小心处理。
注:
1、为了减少篇幅,测试贴出的代码不是完整的代码,但不影响学习参考,另外文中给出的测试结果都是在 chrome(版本:45.0)下运行得出的,在测试结果有差异的情况下,还会给出 IE9,IE10,IE11,firefox(版本:42.0),opera(版本:34.0)的测试结果,没有差异的会在测试结果中说明,不考虑 IE8 及以下。
2、safari 因为设备限制暂不测试,另外它跟 chrome 内核相同,对标准支持的可靠性差不到哪去。
3、老版本的 chrome,firefox,opera 也因为设备的限制无法测试,不过从浏览器对标准的支持程度考虑,这三个浏览器在很早的版本开始对 W3C 的标准都是比较规矩的,加之这些浏览器更新换代的速度较快,现在市面上这些浏览器主流的版本也都是较新的。
4、由于不考虑 IE8 及以下,同时 html 现在都用 html5,所以 document.compatMode = 'BackCompat' 的情况不考虑。不过尽管 BackCompat 模式是 IE6 类的浏览器引出的,但是对于 chrome,firefox 等也存在 document.compatMode = 'BackCompat' 的情况,比如下面的这个网页,你用 chrome 打开,并且在 console 中打印 document.compatMode,你会发现它的值也是 BackCompat(原因跟该页面用的是 html4.0 的 dtd 有关,如果换成 html4.01 的 dtd 就不会在 chrome 和 firefox 里出现该情况了):
http://samples.msdn.microsoft.com/workshop/samples/author/dhtml/refs/compatModeCompat.htm 更多关于 compatMode 的知识,你可以通过下面的几个资源学习:
https://developer.mozilla.org/zh-CN/docs/Web/API/Document/compatMode
https://msdn.microsoft.com/en-us/library/ms533687(VS.85).aspx
http://www.cnblogs.com/uedt/archive/2010/09/21/1832402.html
测试一、验证普通 html 元素(非 body 及 html 根元素)的 offsetWidth、clientWidth、scrollWidth 及相关 height 属性:
- <style type="text/css">
- html,
- body {
- margin: 0;
- }
- body {
- padding: 100px;
- }
- .box {
- overflow: scroll;
- width: 400px;
- height: 300px;
- padding: 20px;
- border: 10px solid #000;
- margin: 0 auto;
- box-sizing: content-box;
- }
- .box-2 {
- border: 1px solid #000;
- }
- </style>
- <body>
- <div class="box">
- <div class="box-2">...</div>
- </div>
- </body>
- <script type="text/javascript">
- var boxE = document.querySelectorAll('.box')[0];
- console.log('scrollWidth:' + boxE.scrollWidth);
- console.log('scrollHeight:' + boxE.scrollHeight);
- console.log('clientWidth:' + boxE.clientWidth);
- console.log('clientHeight:' + boxE.clientHeight);
- console.log('offsetWidth :' + boxE.offsetWidth);
- console.log('offsetHeight:' + boxE.offsetHeight);
- </script>
- <styletype="text/css">
- html,
- body{
- margin: 0;
- }
- body{
- padding: 100px;
- }
- .box{
- overflow: scroll;
- width: 400px;
- height: 300px;
- padding: 20px;
- border: 10px solid #000;
- margin: 0 auto;
- box-sizing: content-box;
- }
- .box-2{
- border: 1px solid #000;
- }
- </style>
- <body>
- <divclass="box">
- <divclass="box-2">...</div>
- </div>
- </body>
- <scripttype="text/javascript">
- var boxE = document.querySelectorAll('.box')[0];
- console.log('scrollWidth:' + boxE.scrollWidth);
- console.log('scrollHeight:' + boxE.scrollHeight);
- console.log('clientWidth:' + boxE.clientWidth);
- console.log('clientHeight:' + boxE.clientHeight);
- console.log('offsetWidth :' + boxE.offsetWidth);
- console.log('offsetHeight:' + boxE.offsetHeight);
- </script>
在这个例子中,box 元素有 400*300 的宽高,20px 的 padding 和 10px 的 border,chrome 下对应的盒模型:
js 执行结果:
从盒模型与 js 执行结果可知:
1)offsetWidth 与 offsetHeight 与 chrome 审查元素看到的尺寸完全一致;
2)clientWidth 与 clientHeight 分别等于 offsetWidth 与 offsetHeight 减掉相应边框(上下共 20px,左右共 20px)和滚动条宽度后的值(chrome 下滚动条宽度为 17px);
3)对于 scrollWidth 由于没有发生横向的溢出,同时由于 overflow: scroll 的原因,scrollWidth 跟 clientWidth 相同,但是没有包含滚动条的宽度,这也验证了前面提出的结论;
4)对于 scrollHeight,在这个例子中,它其实等于上下 padding(共 40px) + div.box-2 的 offsetHeight(1370px),div.box-2:
5)以上测试还有一个 css 值得注意,就是 box-sizing,以上代码中 box-sizing 设置为了 content-box,如果把它改成 border-box,结果也是类似的,因为 offsetWidth,clientWidth 还有 scrollWidth 对应的区域不会发生改变。
6)其它浏览器运行结果与 1-5 的结论一致。
测试二、验证 html 根元素和 body 元素的相关 offset client scroll 宽高属性:
- <style type="text/css">
- html,
- body {
- margin: 0;
- }
- body {
- border: 10px solid #D4D2D2;
- }
- .box {
- overflow: scroll;
- width: 400px;
- height: 300px;
- padding: 20px;
- border: 10px solid #000;
- margin: 0 auto;
- box-sizing: content-box;
- }
- .box-2 {
- border: 1px solid #000;
- }
- </style>
- <body>
- <div class="box">
- <div class="box-2">...</div>
- </div>
- <div class="box">
- <div class="box-2">...</div>
- </div>
- <div class="box">
- <div class="box-2">...</div>
- </div>
- <div class="box">
- <div class="box-2">...</div>
- </div>
- </body>
- <script>
- console.log('docE.scrollWidth:' + document.documentElement.scrollWidth);
- console.log('scrollHeight:' + document.documentElement.scrollHeight);
- console.log('docE.clientWidth:' + document.documentElement.clientWidth);
- console.log('docE.clientHeight:' + document.documentElement.clientHeight);
- console.log('docE.offsetWidth :' + document.documentElement.offsetWidth);
- console.log('docE.offsetHeight:' + document.documentElement.offsetHeight);
- console.log('');
- console.log('body.scrollWidth:' + document.body.scrollWidth);
- console.log('body.scrollHeight:' + document.body.scrollHeight);
- console.log('body.clientWidth:' + document.body.clientWidth);
- console.log('body.clientHeight:' + document.body.clientHeight);
- console.log('body.offsetWidth :' + document.body.offsetWidth);
- console.log('body.offsetHeight:' + document.body.offsetHeight);
- </script>
- <styletype="text/css">
- html,
- body{
- margin: 0;
- }
- body{
- border: 10px solid #D4D2D2;
- }
- .box{
- overflow: scroll;
- width: 400px;
- height: 300px;
- padding: 20px;
- border: 10px solid #000;
- margin: 0 auto;
- box-sizing: content-box;
- }
- .box-2{
- border: 1px solid #000;
- }
- </style>
- <body>
- <divclass="box">
- <divclass="box-2">...</div>
- </div>
- <divclass="box">
- <divclass="box-2">...</div>
- </div>
- <divclass="box">
- <divclass="box-2">...</div>
- </div>
- <divclass="box">
- <divclass="box-2">...</div>
- </div>
- </body>
- <script>
- console.log('docE.scrollWidth:' + document.documentElement.scrollWidth);
- console.log('scrollHeight:' + document.documentElement.scrollHeight);
- console.log('docE.clientWidth:' + document.documentElement.clientWidth);
- console.log('docE.clientHeight:' + document.documentElement.clientHeight);
- console.log('docE.offsetWidth :' + document.documentElement.offsetWidth);
- console.log('docE.offsetHeight:' + document.documentElement.offsetHeight);
- console.log('');
- console.log('body.scrollWidth:' + document.body.scrollWidth);
- console.log('body.scrollHeight:' + document.body.scrollHeight);
- console.log('body.clientWidth:' + document.body.clientWidth);
- console.log('body.clientHeight:' + document.body.clientHeight);
- console.log('body.offsetWidth :' + document.body.offsetWidth);
- console.log('body.offsetHeight:' + document.body.offsetHeight);
- </script>
在这个例子中,body 下一共有 4 个 box 元素(总高度为 360 * 4 = 1440px),body 的宽是自适应的,body 还有 10px 的 border,运行结果如下:
从这个结果可以看到:
1)body 元素由于 10px 边框的原因,所以 clientWidth 比 offsetWidth 少了 20px,这跟前面提到的理论是一致的,但是不可思议的是 body 的 scrollWidth/scrollHeight 竟然等于它的 offsetWidth/offsetHeight,scrollWidth/scrollHeight 是元素滚动区域的宽高度,按照前面给出的范围图来理解,body 的 scrollWidth/scrollHeight 应该小于它的 offsetWidth/offsetHeight 才对;
2)docE 的 scrollWidth 跟 scrollHeight,应该等于 body 元素的 offsetWidth 跟 offsetHeight,从运行结果来看,这一点是符合的,但是 docE 的 clientWidth 竟然等于它的 offsetWidth,按照范围图,docE 的 clientWidth 应该等于 offsetWidth 减去滚动条宽度才对。
其它的浏览器运行结果与 chrome 也有较大的差异:
IE11:
1)IE11 下 body 元素没有出现 chrome 下 body 元素的问题
2)IE11 下 html 根元素也有 chrome 类似的问题
IE10,IE9:
1)IE10,9 下 body 元素没有出现 chrome 下 body 元素的问题
2)IE10,9 下 html 根元素也没有 chrome 类似的问题
firefox:与 IE11 运行结果一致。
opera: 与 chrome 运行结果一致,可能是因为我这个版本的 opera 用的跟 chrome 一样的 webkit 内核的原因。
看起来 IE9 就跟 IE10 是最正常的,实在是有点难以理解,网上搜索很久,也没有找到相关资料来说明这些差异, 最后也只能采取大胆假设的方式,猜测出几个能解释这些问题的原因 :
1) 首先,网页整体的滚动,跟普通 html 元素的滚动不一样,普通 html 元素自身就是滚动对象, 但是对于网页来说,滚动对象不一定是 html 根元素或者 body 元素。因为当 body 内容为空时,body 的高度是 0,html 根元素的高度也是 0,如果这个时候给 html 或 body 加上 overflow: scroll 的 css,会看到滚动条还是出现浏览器窗口的右边跟底边,所以对于网页整体的滚动,理论上,滚动对象应该是 window,而不是 html 元素或者 body 元素!但实际情况并非如此,就测试的浏览器而言:
对于 IE10,IE9,它的滚动对象是 html 根元素,所以它们的 html 根元素的 offset 会包含滚动条的宽度;
对于其它浏览器,滚动对象是 window,所以它们的 html 根元素的 offset 不包含滚动条的宽度。
2)第二,普通元素发生滚动时,滚动内容 = 它的 content 区域 + 它的 padding 区域,当网页整体滚动时,滚动内容应该是 html 根元素!但实际情况也并非如此,就测试的浏览器而言:
对于 IE9,IE10,IE11,firefox,它们的滚动区域是 html 根元素,所以它们的 documentElement 的 scrollWidth 和 scrollHeight 始终表示网页整体的滚动区域大小!
对于 chrome 和 opera,它们的滚动对象是 body 元素,所以它们的 body 的 scrollWidth 和 scrollHeight 始终表示网页整体的滚动区域大小!
3)第三,浏览器始终把 documentElement.clientWidth 和 documentElement.clientHeight 描述为网页可视区域除去滚动条部分的大小,跟网页内容没有关系!
以上的这些推断也并非是毫无道理,就拿滚动对象和滚动区域来说:chrome 下如果要用 js 滚动页面到某个位置,在不使用 window.scrollTo 的条件下,就必须用 document.body.scrollTop = xxx 来处理,而设置 document.documentElement.scrollTop 无效,说明 chrome 的整体滚动区域是由 body 的滚动区域决定的;而 IE11 和火狐下如果要用 js 滚动页面到某个位置,在不使用 window.scrollTo 的条件下,就必须用 document.documentElement.scrollTop = xxx 来处理,设置 document.body.scrollTop 无效,说明 IE11 和火狐的整体滚动区域是由 html 根元素的滚动区域决定的。
2. 利用 JS 准确获取 DOM 对象的大小
常见的场景有:
1)获取整个网页的可视区域的大小,不包括滚动条
2)获取整个网页的大小,包括不可见的滚动区域
3)获取一个普通 html 元素的大小
4)判断元素或网页有无出现滚动条
5)计算滚动条的宽度
下面针对这 5 个场景一一说明,以下代码均 不考虑 IE8 及以下,不考虑 html4 ,另外请注意 viewport 的设置,要保证在移动设备上 visual viewport 与 layout viewport 重合。
1)如何获取整个网页的可视区域的大小,不包括滚动条
- document.documentElement.clientWidth;
- document.documentElement.clientHeight;
- document.documentElement.clientWidth;
- document.documentElement.clientHeight;
2)如何获取整个网页的大小,包括不可见的滚动区域
- function pageWidth() {
- var doc = document.documentElement,
- body = document.body;
- if (doc.clientWidth == window.innerWidth) {
- return doc["clientWidth"];
- }
- return Math.max(
- body["scrollWidth"], doc["scrollWidth"],
- body["offsetWidth"], doc["clientWidth"]
- );
- }
- function pageHeight() {
- var doc = document.documentElement,
- body = document.body;
- if (doc.clientHeight == window.innerHeight) {
- return doc["clientHeight"];
- }
- return Math.max(
- body["scrollHeight"], doc["scrollHeight"],
- body["offsetHeight"], doc["clientHeight"]
- );
- }
- function pageWidth() {
- var doc = document.documentElement,
- body = document.body;
- if (doc.clientWidth == window.innerWidth) {
- return doc["clientWidth"];
- }
- return Math.max(
- body["scrollWidth"], doc["scrollWidth"],
- body["offsetWidth"], doc["clientWidth"]
- );
- }
- function pageHeight() {
- var doc = document.documentElement,
- body = document.body;
- if (doc.clientHeight == window.innerHeight) {
- return doc["clientHeight"];
- }
- return Math.max(
- body["scrollHeight"], doc["scrollHeight"],
- body["offsetHeight"], doc["clientHeight"]
- );
- }
以上出现的 window.innerWidth 和 window.innerHeight 分别用来获取网页包括滚动条的可视区域的宽高,这也是一个兼容性不错的方法,不过从实际开发情况来看,我们需要不包括滚动条的可视区域更多一些,所以在前面没有单独介绍。另外在之前给出的 PPK 的博客中也有关于这两个属性的兼容性测试,可以去了解。
3)如何获取一个普通 html 元素的大小
简单方法:
- docE.offsetWidth;
- docE.offsetHeight;
- docE.offsetWidth;
- docE.offsetHeight;
利用 getBoundingClientRect:
- var obj = docE.getBoundingClientRect(),
- elemWidth,
- elemHeight;
- if(obj) {
- if(obj.width) {
- elemWidth = obj.width;
- elemHeight = obj.height;
- } else {
- elemWidth = obj.right - obj.left;
- elemHeight = obj.bottom - obj.top;
- }
- } else {
- elemWidth = docE.offsetWidth;
- elemHeight = docE.offsetHeight;
- }
- var obj = docE.getBoundingClientRect(),
- elemWidth,
- elemHeight;
- if(obj) {
- if(obj.width) {
- elemWidth = obj.width;
- elemHeight = obj.height;
- } else {
- elemWidth = obj.right - obj.left;
- elemHeight = obj.bottom - obj.top;
- }
- } else {
- elemWidth = docE.offsetWidth;
- elemHeight = docE.offsetHeight;
- }
getBoundingClientRect 将在下篇文章中跟其它与位置有关的 DOM 属性一起再详细介绍。
4 )判断元素或网页有无出现滚动条
- function scrollbarState(elem) {
- var docE = document.documentElement,
- body = document.body;
- if (!elem || elem === document || elem === docE || elem === body) {
- return {
- scrollbarX: docE.clientHeight window.innerHeight,
- scrollbarY: docE.clientWidth window.innerWidth
- }
- }
- if (typeof(Element) == 'function' & !(elem instanceof(Element) || !body.contains(elem))) {
- return {
- scrollbarX: false,
- scrollbarY: false
- };
- }
- var elemStyle = elem.style,
- overflowStyle = {
- hidden: elemStyle.overflow == 'hidden',
- hiddenX: elemStyle.overflowX == 'hidden',
- hiddenY: elemStyle.overflowY == 'hidden',
- scroll: elemStyle.overflow == 'scroll',
- scrollX: elemStyle.overflowX == 'scroll',
- scrollY: elemStyle.overflowY == 'scroll'
- };
- return {
- scrollbarX: overflowStyle.scroll || overflowStyle.scrollX || (!overflowStyle.hidden & !overflowStyle.hiddenX && elem.clientWidth elem.scrollWidth),
- scrollbarY: overflowStyle.scroll || overflowStyle.scrollY || (!overflowStyle.hidden && !overflowStyle.hiddenY && elem.clientHeight elem.scrollHeight)
- };
- }
- function scrollbarState(elem) {
- var docE = document.documentElement,
- body = document.body;
- if (!elem || elem === document || elem === docE || elem === body) {
- return {
- scrollbarX: docE.clientHeight window.innerHeight,
- scrollbarY: docE.clientWidth window.innerWidth
- }
- }
- if (typeof(Element) == 'function' & !(eleminstanceof(Element) || !body.contains(elem))) {
- return {
- scrollbarX: false,
- scrollbarY: false
- };
- }
- var elemStyle = elem.style,
- overflowStyle = {
- hidden: elemStyle.overflow == 'hidden',
- hiddenX: elemStyle.overflowX == 'hidden',
- hiddenY: elemStyle.overflowY == 'hidden',
- scroll: elemStyle.overflow == 'scroll',
- scrollX: elemStyle.overflowX == 'scroll',
- scrollY: elemStyle.overflowY == 'scroll'
- };
- return {
- scrollbarX: overflowStyle.scroll || overflowStyle.scrollX || (!overflowStyle.hidden & !overflowStyle.hiddenX && elem.clientWidth elem.scrollWidth),
- scrollbarY: overflowStyle.scroll || overflowStyle.scrollY || (!overflowStyle.hidden && !overflowStyle.hiddenY && elem.clientHeight elem.scrollHeight)
- };
- }
当 x 或 y 方向的 overflow 为 scroll 的时候,该方向的 scrollbarX 为 true,表示出现滚动条。
5)计算滚动条的宽度
- function scrollbarWidth() {
- var docE = document.documentElement,
- body = document.body,
- e = document.createElement('div');
- e.style.cssText = 'position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll;';
- body.appendChild(e);
- var _scrollbarWidth = e.offsetWidth - e.clientWidth
- body.removeChild(e);
- return _scrollbarWidth;
- }
- function scrollbarWidth() {
- var docE = document.documentElement,
- body = document.body,
- e = document.createElement('div');
- e.style.cssText = 'position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll;';
- body.appendChild(e);
- var _scrollbarWidth = e.offsetWidth - e.clientWidth
- body.removeChild(e);
- return _scrollbarWidth;
- }
以上就是本文的全部内容,希望能对您有所帮助:)另外本文第二部分提供的代码,是根据个人思考和经验总结出的一些方法,在兼容性方面可能还有未考虑到的地方, 如果您有遇到其它不兼容的情况或者有更好的代码,还请不吝赐教 ,欢迎您的指导。
来源: http://www.phperz.com/article/17/0408/267886.html