您好,欢迎访问一九零五行业门户网

JavaScript位置与大小(1)之正确理解和运用与尺寸大小相关的DOM属性_javascript技巧

在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属性:
...
...

在这个例子中,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宽高属性:
...
...
...
...
...
...
...
...

在这个例子中,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;}
以上就是本文的全部内容,希望能对您有所帮助:)另外本文第二部分提供的代码,是根据个人思考和经验总结出的一些方法,在兼容性方面可能还有未考虑到的地方, 如果您有遇到其它不兼容的情况或者有更好的代码,还请不吝赐教 ,欢迎您的指导。
其它类似信息

推荐信息