event.pageX 和 event.pageY 属性表示鼠标到网页的左边和上边的距离。当浏览器没有卷动的时候,它们
的数值等于 event.clientX 和 event.clientY 值。我们进行验证:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
.box{
width:200px;
height:200px;
background-color: orange;
margin-top: 100px;
margin-left:200px;
}
</style>
</head>
<body>
<div class="box" id="box"></div>
<script type="text/javascript">
var oBox = document.getElementById("box");
oBox.onmousemove = function(event){
console.log(event.clientX , event.clientY , event.pageX , event.pageY);
}
</script>
</body>
</html>
打开网页,鼠标指针在盒子内自由移动,可以看到 event.clientX 始终等于 event.pageX 值,
event.clientY 始终等于 event.pageY 值。
当网页非常长,产生了滚动条,我们滚动了滚动条时,event.pageY 和 event.clientY 值产生了区别。例
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
.box{
width:200px;
height:200px;
background-color: orange;
margin-top: 600px;
margin-left:200px;
}
</style>
</head>
<body>
<div class="box" id="box"></div>
<script type="text/javascript">
var oBox = document.getElementById("box");
oBox.onmousemove = function(event){
console.log(event.clientY , event.pageY);
}
</script>
</body>
</html>
为了使网页产生滚动条,我们给盒子设置了非常大的 margin-top 值,网页被“撑”长了。打开浏览器查看
网页,故意卷动一些浏览器的滚动条,将鼠标指针随便在盒子内部自由自动,此时可以看出 event.pageY 大于
event.clientY 值,这个因为前者表示鼠标指针到页面顶端的距离;而后者表示鼠标指针到浏览器视口顶端的
距离
嵌套盒模型的鼠标位置 offsetX/Y 的问题
问题引出
我们知道鼠标事件中,event.offsetX/Y 表示鼠标指针到盒子的左边、顶边的距离。如果是单一盒模型,
这没有任何问题。但是如果盒子有内部子盒子,即盒模型为嵌套状态时,就会产生一定的问题。我们通过案例代
码来演示。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
.box{
width:200px;
height:200px;
background-color: orange;
margin-top: 200px;
margin-left:200px;
border:1px solid #eee;
}
p{
width:80px;
height:80px;
background-color: blue;
margin: 30px;
}
</style>
</head>
<body>
<div class="box" id="box">
<p></p>
</div>
<script type="text/javascript">
var oBox = document.getElementById("box");
oBox.onmousemove = function(event){
console.log(event.offsetX , event.offsetY);
}
</script>
</body>
</html>
我们在 div 盒子内部添加了 p 标签,并且给 p 设置了外边距,此时 p 和 div 之间有一定的距离。我们监听
div 盒子的鼠标移动事件,输出 event.offsetX 和 event.offsetY。打开浏览器,鼠标指针在盒子内部自由移
动,发现当鼠标指针移动到 p 标签的左上角的时候两个数值均区域 0
这个演示可以说明,event.offsetX 和 event.offsetY 的参考点是最内层盒子的左上角,而不是添加事件
监听的盒子的左上角。
问题解决方案
那如何得到鼠标指针距离 div 盒子左上角的位置呢?你可能会回答,将鼠标指针此时的 event.offsetX 值
加上 p 距离 div 盒子左边的距离,event.offsetY 值加上 p 距离 div 盒子上边的距离不就行了。
你的回答是正确的,但是不实用。比如我们下小节将学习拖拽效果,拖拽效果的实现原理就是用 JavaScript
根据鼠标指针来设置 p 标签的位置。即,现在我们要根据鼠标指针的位置来“设置”p 标签的位置,但是鼠标指
针的位置此时还要根据 p 标签和 div 盒子的位置来计算,这就非常矛盾。好比一个人又当运动员也当裁判员,
这是不合适的。
那如何解决这个问题呢?我们可以使用间接的方法来得到鼠标指针距离 div 盒子左上角的位置:
鼠标指针距离 div 上边的距离 = 鼠标指针距离网页顶端的距离 - div 上边距离网页顶端的距离
这个公式的含义如图 11-19 所示。在图 11-19 中,线段 y 就是要求的鼠标指针距离 div 盒子上边的距离,
线段 y1 就是 div 上边距离网页顶端的距离,线段 y2 就是鼠标指针距离网页顶端的距离。
所以上述公式可以简化为 y = y2 - y1。
y1 的值是 event.pageY,但是 y1 的值用 JavaScript 怎么表达呢?即一个盒子距离页面顶端的距离应该
如何得到呢?我们把一个盒子的顶边(或左边)距离网页顶边(或左边)的距离,称为这个盒子的“竖直净位置”
(或水平净位置)。
净位置的计算
JavaScript 中任何一个 DOM 元素,都有一个 offsetTop 属性,表示这个盒子顶边到自己的“定位参考祖
先元素”顶边的距离。
“定位参考祖先元素”是什么呢?我们把一个盒子的祖先盒子中,离自己最近的已经定位的祖先元素称为“定
位参考祖先元素”,JavaScript 中用 oDiv.offsetParent 属性表示。
比如:
<div id="box1"> → 绝对定位
<div id="box2"> → 相对定位
<div id="box3"> → 没有定位
<p id="para"></p>
</div>
</div>
</div>
最内层的 id 为 para 的 p 标签有 3 个 div 祖先,box3 离 para 最近,但是它没有定位,所以它不是
para.offsetParent。而 box2 是距离 b 最近的已经定位的元素,不管它是相对定位还是绝对定位,只要定位就
行,它就是 para.offsetParent。
我们做实验来验证。HTML 和 CSS 如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
.box1{
position: absolute;
width:500px;
height: 400px;
border:3px solid red;
padding:40px;
}
.box2{
position: relative;
width:400px;
height: 300px;
border:3px solid red;
padding:40px;
}
.box3{
width:200px;
height: 200px;
border:3px solid red;
padding:50px;
}
#para{
width: 60px;
height: 60px;
background-color: orange;
}
</style>
</head>
<body>
<div class="box1">
<div class="box2">
<div class="box3">
<p id="para"></p>
</div>
</div>
</div>
</body>
</html>
页面布局完毕,网页如图 11-20 所示。最内层的方形盒子是 p 标签,包裹它的是 3 个 div 盒子。从内层到
外层的 div 盒子的定位情况分别是:没有定位、相对定位、绝对定位。根据我们已经学习过的知识,box2 就是p 标签的“定位参考祖先元素”即 para.offsetParent。而 p 标签到 box2 的距离就是 para.offsetTop。
我们书写程序来测试:
<script type="text/javascript">
var para = document.getElementById("para");
alert(para.offsetTop);
alert(para.offsetParent.className);
</script>
可见,box2 的确是 para 的定位参考祖先盒子。并且弹出的 93 就是 para 到这个盒子的距离,如图 11-22
所示。
我们得到了 para 到 box2 的距离,那 para 到页面顶端的距离呢?
容易想到,box2 也有自己的 offsetTop 值,也就是 box2 到它的定位参考盒子 box1 的距离。而 box1 也有
自己的 offsetTop 值,也就是 box1 到它的定位参考盒子 body 的距离。
即,一个盒子的净位置等于它的 offsetTop 加上它的 offsetParent 的 offsetTop,在加上它的
offsetParent 的 offsetParent 的 offsetTop……当然,不要忘记将这些盒子的上边框的宽度加上,因为
offsetTop 值不包括边框。根据这个原理,我们可以写出函数:
function getAllY(o){
var allY = o.offsetTop; //累加器
//一层一层迭代上去,记得补边框
while(o = o.offsetParent){
allY += o.offsetTop + Number(getComputedStyle(o)["border-top-width"]);
}
return allY;
}
程序中的 getComputedStyle(o)["border-top-width"]表示得到这个盒子的上边框的计算后宽度。