此坑消耗我好久时间,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
20scope.showPopover = function (e){
$popover = angular.element(template); // template是一串html文本,这行代码返回一个jquery对象
$compile($popover)(scope); // 使用angular编译器编译
console.log($popover);
console.log($popover.height()); // 输出 0
console.log($popover[0].clientHeight); // 输出 0
$container.append($popover); // container是另外一个jquery对象,用于放popover
var top = e.pageY - $popover.height();
var left = e.pageX - $popover.width(); // 根据鼠标,把弹出框放在合适的位置
console.log($popover);
console.log($popover.height()); // 输出 133
console.log($popover[0].clientHeight); // 输出 0
$popover
.css('top', top + 'px')
.css('left', left + 'px');
$popover.css('display', 'block'); // 因为是popover,所以该窗口由html里面的class控制它默认是不显示的,现在让它显示
console.log($popover);
console.log($popover.height()); // 输出 133
console.log($popover[0].clientHeight); // 输出 133
}
吊诡的问题在于,无论我打开哪个$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
20scope.showPopover = function (e){
$popover = angular.element(template); // template是一串html文本,这行代码返回一个jquery对象
$compile($popover)(scope); // 使用angular编译器编译
console.log($popover);
console.log($popover.height()); // 输出 0
console.log($popover[0].clientHeight); // 输出 0
$container.append($popover); // container是另外一个jquery对象,用于放popover
var top = e.pageY - $popover.height();
var left = e.pageX - $popover.width(); // 根据鼠标,把弹出框放在合适的位置
console.log($popover);
console.log($popover.height()); // 输出 133
console.log($popover[0].clientHeight); // 输出 0
$popover
.css('top', top + 'px')
.css('left', left + 'px');
//$popover.css('display', 'block'); // 因为是popover,所以该窗口由html里面的class控制它默认是不显示的,现在让它显示
console.log($popover);
console.log($popover.height()); // 输出 133
console.log($popover[0].clientHeight); // 输出 0
}
任意一个popover的输出都是这个样子的:
难道后执行的代码会影响前面已经执行的代码的输出?这点太不可思议了!
最诡异的一个测试用例的代码是这样写的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22scope.showPopover = function (e){
$popover = angular.element(template); // template是一串html文本,这行代码返回一个jquery对象
$compile($popover)(scope); // 使用angular编译器编译
console.log($popover);
console.log($popover.height()); // 输出 0
console.log($popover[0].clientHeight); // 输出 0
$container.append($popover); // container是另外一个jquery对象,用于放popover
var top = e.pageY - $popover.height();
var left = e.pageX - $popover.width(); // 根据鼠标,把弹出框放在合适的位置
console.log($popover);
console.log($popover.height()); // 输出 133
console.log($popover[0].clientHeight); // 输出 0
$popover
.css('top', top + 'px')
.css('left', left + 'px');
setTimeout(function (){
$popover.css('display', 'block'); // 因为是popover,所以该窗口由html里面的class控制它默认是不显示的,现在让它显示
console.log($popover);
console.log($popover.height()); // 输出 133
console.log($popover[0].clientHeight); // 输出 133
}, 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
如果有我错误或者理解不当的地方,还请各路豪杰在评论区指出!