此坑消耗我好久时间,mark一下。
1.需求描述
最近项目需要,要做一个根据鼠标事件位置显示popover的功能效果。要在鼠标点击位置之上,居中显示一个template的div,
因此我需要获得这个div的长宽。
2.遇到的问题
在获得这个div的长宽问题上,经历了一坑再坑的过程。就连chrome本身都有一个诡异的问题要戏耍我一下。
1.对于dom元素、jquery元素容易搞混
由于项目采用的angular,在jquery之上又封装了一层,对于没有系统用过jquery的来说,直接使用angular,很多概念确实容易混得更加厉害。
属性、方法属于原生dom的范畴还是jquery的范畴亦或是angular的范畴这一点就会比较混乱,在使用上也被坑了一下。好在这一点通过阅读各种资料可以克服,
另外,angularjs的官方文档也有说明, angular.element()返回的是jquery对象。

2.在dom对象插入前,无论如何获取不了这个dom对象的长宽的
这点导致我纠结了好久popover代码的显示逻辑
3.在dom对象插入以后,如果是隐藏的元素,通过jquery和dom对象获取的数据不一样
4.chrome调试器输出的jquery对象里面的值是根据你展开这个对象的时候再去获取的
在你打开之前,里面属性的状态和值是不确定的,但当你打开后就锁定了(薛定谔的猫,是你吗?)。
这点着实是大坑啊,在调试的时候这点太坑人了!也不知道算不算是chrome的bug。
起初我的代码是这么写的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| scope.showPopover = function (e){ $popover = angular.element(template); $compile($popover)(scope); console.log($popover); console.log($popover.height()); console.log($popover[0].clientHeight); $container.append($popover); var top = e.pageY - $popover.height(); var left = e.pageX - $popover.width(); console.log($popover); console.log($popover.height()); console.log($popover[0].clientHeight); $popover .css('top', top + 'px') .css('left', left + 'px'); $popover.css('display', 'block'); console.log($popover); console.log($popover.height()); console.log($popover[0].clientHeight); }
|
吊诡的问题在于,无论我打开哪个$popover的输出,里面的结构都是这样的:

明显clientHeight有值啊,为啥在display以前输出都是0,这简直就是我定义一个变量为1,它死活就输出0!?(这个问题后文还会涉及到)
然后我把display这一行注释掉:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| scope.showPopover = function (e){ $popover = angular.element(template); $compile($popover)(scope); console.log($popover); console.log($popover.height()); console.log($popover[0].clientHeight); $container.append($popover); var top = e.pageY - $popover.height(); var left = e.pageX - $popover.width(); console.log($popover); console.log($popover.height()); console.log($popover[0].clientHeight); $popover .css('top', top + 'px') .css('left', left + 'px'); console.log($popover); console.log($popover.height()); console.log($popover[0].clientHeight); }
|
任意一个popover的输出都是这个样子的:
难道后执行的代码会影响前面已经执行的代码的输出?这点太不可思议了!
最诡异的一个测试用例的代码是这样写的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| scope.showPopover = function (e){ $popover = angular.element(template); $compile($popover)(scope); console.log($popover); console.log($popover.height()); console.log($popover[0].clientHeight); $container.append($popover); var top = e.pageY - $popover.height(); var left = e.pageX - $popover.width(); console.log($popover); console.log($popover.height()); console.log($popover[0].clientHeight); $popover .css('top', top + 'px') .css('left', left + 'px'); setTimeout(function (){ $popover.css('display', 'block'); console.log($popover); console.log($popover.height()); console.log($popover[0].clientHeight); }, 10000); }
|
只要我在10秒内打开这个popover里面的内容,里面将永远展示第二张图的结果
只要我在10秒后打开这个popover里面的内容,里面将永远展示第一张图的结果
看来对于jquery对象这里面的输出难道还是根据你查看的时间定的?
3.总结
1.在dom对象或者是jquery对象还没有插入到页面中时,无论是通过jquery中的height()或者width()方法,
或者直接通过dom对象中的各种height包括(clientHeight, offsetHeight, scrollHeight)都是无法获得它的高度值的
stackoverflow上这个解答印证了这一点
http://stackoverflow.com/questions/7693244/javascript-jquery-how-to-get-the-width-of-an-element-css-class-that-hasnt-b
2.在对象style的display属性为none的作用下,也会一定程度上影响这个jquery对象的长宽取值
具体如下:
2.1 从jquery对象的height()和width()方法可以获取到隐藏元素的长宽
2.2 dom元素上的各种height和width属性的值均为0
如果有我错误或者理解不当的地方,还请各路豪杰在评论区指出!