JS | JS之元素偏移量 offset 系列属性详解

目录

一、offset 概述

定位父级

offsetParent 

偏移量

offsetWidth

offsetHeight

offsetLeft

offsetTop

计算页面偏移

注意事项

二、offset 与 style 区别

偏移offset

样式style

三、案例

★ 案例:获取鼠标在盒子内的坐标

★ 案例:模态框拖拽

★ 案例:仿京东放大镜

四、番外:元素视图方法


偏移量(offset dimension)是javascript中的一个重要的概念。涉及到偏移量的主要是offsetLeft、offsetTop、offsetHeight、offsetWidth这四个属性。当然,还有一个偏移参照——定位父级offsetParent。本文将详细介绍该部分内容。

一、offset 概述

offset 翻译过来就是偏移量, 我们使用 offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等。

  • 获得元素距离带有定位父元素的位置

  • 获得元素自身的大小(宽度高度)

注意返回的数值都不带单位

♠ offset 系列常用属性 ♠

以上五个偏移量属性,它们都是只读属性。如果元素设置了display:none,则它的偏移量属性均为0。每次访问偏移量属性都需要重新计算,重复访问偏移量属性需耗费大量性能,如需重复访问,应将其值保存在变量中,以提高性能。 

参考分析图

示例代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><style>* {margin: 0;padding: 0;}.father {margin: 100px;width: 200px;height: 200px;background-color: pink;}.son {margin-left: 30px;width: 100px;height: 100px;background-color: purple;}.box {margin: 0 100px;width: 200px;height: 200px;padding: 20px;background-color: skyblue;border: 10px solid red;}</style></head><body><div class="father"><div class="son"></div></div><div class="box"></div><script>// offset 系列let father = document.querySelector(".father");let son = document.querySelector(".son");// 1. offsetParent返回带有定位的父亲,否则返回的是bodyconsole.log(son.offsetParent); // 返回<body>...</body>// 注意:offsetParent与parentNode的值不一定相等// 返回最近一级的父亲(亲爸爸) 不管父亲有没有定位console.log(son.parentNode); // 返回<div class="father">...</div>// 2. 可以得到元素的偏移量(位置),返回的是不带单位的数值console.log(father.offsetTop); // 100 ,因为css为其设置的margin为100console.log(father.offsetLeft); // 100 ,因为css为其设置的margin为100// 它以带有定位的父亲为准,如果没有父亲或者父亲没有定位 则以 body 为准console.log(son.offsetLeft); // 130 ,因为虽然其有父亲,但是其父亲没有定位,所以以body为准,即100+30=130// 3. 可以得到元素的大小(宽度和高度),是包含padding + border + width/height的,不包括marginlet box = document.querySelector(".box");console.log(box.offsetWidth); // 260console.log(box.offsetHeight); // 260</script></body>
</html>

下面我们把offset的5个偏移属性,分别一一介绍一下

定位父级

offsetParent 

offsetParent属性是一个偏移参照,翻译为定位父级,即当前元素最近的有定位的父级元素(position不等于static)。定位父级分为下列四种情况。

在理解偏移大小之前,首先要理解offsetParent。人们并没有把offsetParent翻译为偏移父级,而是翻译成定位父级,很大原因是offsetParent与定位有关。

(1)当前元素自身有fixed定位,offsetParent返回null

当元素自身有fixed固定定位时,我们知道固定定位的元素相对于视口进行定位,此时没有定位父级,offsetParent的结果为null。

注意:firefox浏览器有兼容性问题——firefox没有考虑固定定位问题,返回body元素

<!-- 元素自身有fixed定位,offsetParent的结果为null -->
<div id="box" style="position:fixed;"></div>
<script>let box = document.querySelector('#box');console.log(box.offsetParent); // null//兼容问题:firefox浏览器没有考虑固定定位的问题,返回body元素,其他浏览器都返回null
</script>

(2)当前元素自身无fixed定位,且不存在有定位的父级元素,offsetParent返回body元素

<!-- 元素自身无fixed定位,且父级元素都未经过定位,offsetParent的结果为<body> -->
<div><span id="box"></span>
</div>
<script>let box = document.querySelector('#box');console.log(box.offsetParent); // <body>...</boby>
</script>

(3)当前元素自身无fixed定位,且存在有定位的父级元素,offsetParent返回最近的有定位的父级元素

<!--元素自身无fixed定位,且父级元素存在经过定位的元素,offsetParent的结果为离自身元素最近的经过定位的父级元素-->
<div id="div0" style="position:absolute;"><div id="div1" style="position:absolute;"><span id="box"></span></div>
</div>
<script>let box = document.querySelector('#box');console.log(span.offsetParent); // <div id="div1" style="position:absolute;">...</div>
</script>

(4)body元素无父元素节点,offsetParent返回null 

<!-- <body>元素的parentNode是null -->
<script>console.log(document.body.offsetParent); // null
</script>

★ IE7 浏览器Bug

对于定位父级offsetParent来说,IE7-浏览器存在以下bug

【bug1】元素本身经过绝对定位或相对定位,且父级元素无定位时,IE7-浏览器下offsetParent返回的结果是<html>

<div id="test" style="position:absolute;"></div>    
<script>
//IE7-浏览器返回<html>,其他浏览器返回<body>
let box = document.querySelector('#test');
console.log(box.offsetParent);
</script>
<div id="test" style="position:relative;"></div>    
<script>
//IE7-浏览器返回<html>,其他浏览器返回<body>
let box = document.querySelector('#test');
console.log(box.offsetParent);
</script>
<div id="test" style="position:fixed;"></div>    
<script>
// firefox并没有考虑固定定位的问题,返回<body>,其他浏览器都返回null
let box = document.querySelector('#test');
console.log(box.offsetParent);
</script>

 【bug2】如果父级元素存在触发haslayout的元素或有定位的元素,则offsetParent的结果为离自身元素最近的有定位或触发haslayout的父级元素

注意:关于haslayout的详细信息haslayout详解 - 小火柴的蓝色理想 - 博客园

<div id="div0" style="display:inline-block;"><div id='test'></div>    
</div>
<script>
let box = document.querySelector('#test');
//IE7-浏览器返回<div id="div0">,其他浏览器返回<body>
console.log(box.offsetParent);
</script>
<div id="div0" style="position:absolute;"><div id="div1" style="display:inline-block;"><div id='test'></div>    </div>    
</div>
<script>
let box = document.querySelector('#test');
//IE7-浏览器返回<div id="div1">,其他浏览器返回<div id="div0">
console.log(box.offsetParent);
</script>
<div id="div0" style="display:inline-block;"><div id="div1" style="position:absolute;"><div id='test'></div>    </div>    
</div>
<script>
let box = document.querySelector('#test');
//所有浏览器都返回<div id="div1">
console.log(box.offsetParent);
</script>

偏移量

偏移量共包括offsetHeight、offsetWidth、offsetLeft、offsetTop这四个属性

参考:彻底搞懂clientHeight、offsetHeight、scrollHeight的区别

offsetWidth

获取dom元素视觉上的宽度,返回结果是一个数字,单位像素。(不包含外边距

offsetWidth表示元素在水平方向上占用的空间大小,无单位(以像素px计)

<script>// 边框/内边距/宽/垂直滚动条element.offsetWidth = border + padding + width + scroll 
<script>
offsetHeight

获取dom元素视觉上的高度,返回结果是一个数字,单位像素。(不包含外边距

offsetHeight表示元素在垂直方向上占用的空间大小,无单位(以像素px计)

<script>// 边框/内边距/高/水平滚动条element.offsetWidth = border + padding + height + scroll; 
<script>

示例:offsetWidth / offsetHeight

<!DOCTYPE html>
<html lang="en"><head><title></title><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><style>div {width: 100px;height: 200px;padding: 10px;margin: 20px;border: 5px solid black;}</style></head><body><div id="box"></div><script>let box = document.querySelector("#box");// element.offsetWidth =  border-left-width + padding-left + width + padding-right + border-right-width; console.log(box.offsetWidth); // 130=5*2+10*2+100// element.offsetHeight =  border-top-width + padding-top + height + padding-bottom + border-bottom-widthconsole.log(box.offsetHeight); // 130=5*2+10*2+200</script></body>
</html>

注意:如果存在垂直滚动条,offsetWidth也包括垂直滚动条的宽度;如果存在水平滚动条,offsetHeight也包括水平滚动条的高度

有滚动条时,offsetWidth和offsetHeight包含滚动条的宽度。IE8浏览器将垂直和水平滚动条的宽度计算在width宽度和height高度中,所以出现滚动条时width和height不变,而其他浏览器则是把垂直滚动条的宽度从width宽度中移出,把水平滚动条的高度从height高度中移出。

<!DOCTYPE html>
<html lang="en"><head><title></title><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><style>#box {width: 100px;height: 100px;padding: 10px;margin: 10px;border: 1px solid black;overflow: scroll;}</style></head><body><div id="box"></div><script>//IE8-浏览器将垂直滚动条的宽度计算在width宽度和height高度中,width和height的值仍然是100px;//而其他浏览器则把垂直滚动条的宽度从width宽度中移出,把水平滚动条的高度从height高度中移出,因为滚动条默认宽度为17px,所以width宽度和height高度为剩下的83pxlet box = document.querySelector("#box");if (window.getComputedStyle) {// 83px 83pxconsole.log(getComputedStyle(box).width, getComputedStyle(box).height);} else {//ie8及以下版本的浏览器console.log(box.currentStyle.width, box.currentStyle.height); //100px}// 122=border*2+padding*2+100console.log(box.offsetWidth, box.offsetHeight); </script></body>
</html>

执行结果,如下图:

有童鞋可能会疑惑,宽度和高度不应该是83px吗?怎么各自多出了0.2px?我们看一下浏览器中呈现的样式。

盒子模型中,元素宽高的确是多出了0.2px,而且边框少了0.2,这是因为我的电脑系统缩放与布局调的是125%,这就是症结所在。所以将系统缩放调整到100%,这样浏览器就可以正确显示‌了。

补:currentStyle是一个在旧版本的IE浏览器中引入的属性,用于获取元素计算后的样式。‌ 它现在已经被废弃,仅支持IE浏览器使用‌。

currentStyle属性用于获取指定元素的当前样式,返回的是一个样式属性对象。它可以查询元素的宽度、高度、文本对齐方式、位置等所有CSS属性值。然而,这个属性仅能在IE浏览器中使用,如果需要在其他浏览器(如Firefox)中实现相同的效果,应使用getComputedStyle属性‌。

补:浏览器滚动条默认的宽度和高度通常是17px。‌ 这是因为在IE7及以上版本的IE浏览器、Chrome和Firefox中,滚动栏所占用的宽度都是17px‌。

不同浏览器的默认滚动条宽度可能有所不同,例如在Chrome或Safari中,可以通过设置::-webkit-scrollbar的宽度来调整滚动条的宽度。例如:

::-webkit-scrollbar {width: 4px;
}

这段代码将Chrome或Safari浏览器的滚动条宽度设置为4px‌。

在Firefox中,可以通过设置scrollbar-width属性来隐藏或显示滚动条。例如:

scrollbar-width: none;

这段代码将隐藏Firefox浏览器的滚动条‌。

在IE浏览器中,可以通过设置-ms-overflow-style属性来隐藏滚动条。例如:

-ms-overflow-style: none;

这段代码将隐藏IE浏览器的滚动条‌

番外:网页尺寸offsetHeight、offsetWidth
offsetHeight和offsetWidth,获取网页内容高度和宽度(包括滚动条等边线,会随窗口的显示大小改变)。

① 值

offsetHeight = clientHeight + 水平滚动条 + 边框。
offsetWidth = clientWidth + 滚动条 + 边框。

② 浏览器兼容性

var w= document.documentElement.offsetWidth || document.body.offsetWidth;
var h= document.documentElement.offsetHeight || document.body.offsetHeight;

offsetLeft

获取当前元素与其最近的有定位父级元素之间的x轴距离(计算左上顶点)。若当前元素没有有定位父级元素,获取其距离文档的x轴距离。返回结果是一个数字,单位像素。

offsetLeft表示元素的左外边框至offsetParent元素的左内边框之间的像素距离

offsetTop

获取当前元素与其最近的有定位父级元素之间的y轴距离(计算左上顶点)。若当前元素没有有定位的父级元素,获取其距离文档的y轴距离。返回结果是一个数字,单位像素。

offsetTop表示元素的上外边框至offsetParent元素的上内边框之间的像素距离

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><style>div#out {padding: 5px;position: relative;background-color: red;margin: 6px;border: 1px solid black;}div#inline {width: 50px;height: 50px;margin: 10px;background-color: green;}</style></head><body><div id="out"><div id="inline"></div></div><script>//parseInt(getComputedStyle(inline,null)['marginTop']) +//parseInt(getComputedStyle(out,null)['paddingTop'])=10+5=15console.log(inline.offsetTop); //15//parseInt(getComputedStyle(inline,null)['marginLeft']) +//parseInt(getComputedStyle(out,null)['paddingLeft'])=10+5=15console.log(inline.offsetLeft); //15</script></body>
</html>

★ IE7 浏览器Bug

IE7-浏览器在offsetTop属性的处理上存在bug

【1】若父级设置position: relative,则在IE7-浏览器下,offsetTop值为offsetParent元素的paddingBottom值

<div id="out" style="padding: 5px;position: relative;"><div id="test" style="width:100px; height:100px; margin:10px;"></div>        
</div>
<script>
// 其他浏览器返回15(5+10),而IE7-浏览器返回5
console.log(test.offsetTop);
</script>

【2】若父级设置position: aboslute(或其他触发haslayout的条件),offsetTop值为offsetParent元素的paddingBottom值和当前元素的marginTop值的较大值

<div id="out" style="padding: 5px;position:absolute;"><div id="test" style="width:100px; height:100px; margin:10px;"></div>        
</div>
<script>
// 其他浏览器返回15(5+10),而IE7-浏览器返回10(10和5的较大值)
console.log(test.offsetTop);
</script>

—— 小结:偏移量 ——

偏移量:包括元素在屏幕上占用的所有可见空间,元素的可见大小有其高度,宽度决定,包括所有内边距,滚动条和边框大小(注意,不包括外边距)。

以下4个属性可以获取元素的偏移量

1. offsetHeight:元素在垂直方向上占用的空间大小,以像素计。包括元素的高度(可见的),水平滚动条的高度,上边框高度和下边框高度。

2. offsetWidth:元素在水平方向上占用的空间大小,以像素计。包括元素的宽度(可见的),垂直滚动条的宽度,左边框宽度和右边框宽度。

3: offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离。

4: offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离。

其中offsetLeft,offsetTop属性与包含元素有关,包含元素的引用保存在offsetParent中,请注意offsetParent与parentNode的值不一定相等

计算页面偏移

计算元素在页面上的偏移量只需将当前元素的offsetLeft和offsetTop与其offsetParent的相同属性相加,再加上offsetParent相应方向的边框,如此一直循环到根元素,就可以得到元素到页面的偏移量。

注意:在默认情况下,IE8 浏览器下如果使用currentStyle()方法获取<html>和<body>(甚至普通div元素)的边框宽度都是medium,而如果使用clientLeft(或clientTop)获取边框宽度,则是实际的数值

function getElementLeft(element){var actualLeft = element.offsetLeft;var current = element.offsetParent;while(current != null){actualLeft += current.offsetLeft + current.clientLeft;current = current.offsetParent;}return actualLeft + 'px';
}
function getElementTop(element){var actualTop = element.offsetTop;var current = element.offsetParent;while(current != null){actualTop += current.offsetTop + current.clientTop;current = current.offsetParent;}return actualTop + 'px';
}

完整示例代码如下:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><style>html,body {border: 0;}body {margin: 0;}</style></head><body><div style="padding: 20px; border: 1px solid black; position: absolute"><div id="test" style="width: 100px; height: 100px; margin: 10px"></div></div><script>function getElementLeft(element) {var actualLeft = element.offsetLeft;var current = element.offsetParent;while (current != null) {actualLeft += current.offsetLeft + current.clientLeft;current = current.offsetParent;}return actualLeft + "px";}function getElementTop(element) {var actualTop = element.offsetTop;var current = element.offsetParent;while (current != null) {actualTop += current.offsetTop + current.clientTop;current = current.offsetParent;}return actualTop + "px";}//其他浏览器返回31(10+20+1),而IE7-浏览器返回21((20和10的较大值)+1)console.log(getElementTop(test));//所有浏览器返回31(10+20+1)console.log(getElementLeft(test));</script></body>
</html>

注意事项

【1】所有偏移量属性都是只读的

<div id="test" style="width:100px; height:100px; margin:10px;"></div>        
<script>
console.log(test.offsetWidth);//100
//IE8-浏览器会报错,其他浏览器则静默失败
test.offsetWidth = 10;
console.log(test.offsetWidth);//100
</script>

【2】如果给元素设置了display:none,则它的偏移量属性都为0

<div id="test" style="width:100px; height:100px; margin:10px;display:none"></div>
<script>
console.log(test.offsetWidth);//0
console.log(test.offsetTop);//0
</script>

 【3】每次访问偏移量属性都需要重新计算

<div id="test" style="width:100px; height:100px; margin:10px;"></div>        
<script>
console.time("time");
for(var i = 0; i < 100000; i++){var a = test.offsetWidth;
}
console.timeEnd('time');//65.129ms
</script>
<div id="test" style="width:100px; height:100px; margin:10px;"></div>        
<script>
console.time("time");
var a = test.offsetWidth;
for(var i = 0; i < 100000; i++){var b = a;
}
console.timeEnd('time');//1.428ms
</script>

由上面代码对比可知,重复访问偏移量属性需要耗费大量的性能,所以要尽量避免重复访问这些属性。如果需要重复访问,则把它们的值保存在变量中,以提高性能。


—— 小结:‌offset偏移属性 ——

‌offset偏移主要包括以下几个属性:​ offsetLeft、offsetTop、offsetWidth、offsetHeight ​和offsetParent。‌

  • offsetLeft‌:表示元素的左外边框至包含元素的左内边框之间的像素距离。
  • offsetTop‌:表示元素的上外边框至包含元素的上内边框之间的像素距离。
  • offsetWidth‌:表示元素在水平方向上占用的空间大小,包括元素的宽度(可见的)、垂直滚动条的宽度、左边框宽度和右边框宽度。
  • offsetHeight‌:表示元素在垂直方向上占用的空间大小,包括元素的高度(可见的)、水平滚动条的高度、上边框高度和下边框高度。
  • offsetParent‌:表示当前元素最近的有定位的父级元素,即position不等于static的父元素。如果元素自身有fixed定位,offsetParent结果为null;如果元素自身无fixed定位且父级元素存在经过定位的元素,offsetParent的结果为离自身元素最近的经过定位的父级元素。

这些属性用于获取或设置元素相对于其最近祖先或文档边界框的偏移量,常用于动态获取元素的位置和大小信息‌。 

二、offset 与 style 区别

偏移offset

  • offset 可以得到任意样式表中的样式值

  • offset 系列获得的数值是没有单位的

  • offsetWidth 包含padding+border+width,不含外边距

  • offsetWidth 等属性是只读属性,只能获取不能赋值

所以,我们想要获取元素大小位置,用offset更合适

样式style

  • style 只能得到行内样式表中的样式值

  • style.width 获得的是带有单位的字符串

  • style.width 获得不包含padding和border 的值

  • style.width 是可读写属性,可以获取也可以赋值

所以,我们想要给元素更改值,则需要用style改变

<body><div class="box" style="width: 200px;"></div><script>// offset与style的区别var box = document.querySelector('.box');console.log(box.offsetWidth); // 不带单位的数字 paddiing+border+widthconsole.log(box.style.width); // 只能得到行内样式 带单位的字符串 width// box.offsetWidth = '300px'; // 只能获取不能赋值box.style.width = '300px'; // 可以获取可以赋值// offset更合适获取元素大小位置// style更适合给元素更改值</script>
</body>

三、案例

★ 案例:获取鼠标在盒子内的坐标

  1. 我们在盒子内点击,想要得到鼠标距离盒子左右的距离。
  2. 首先得到鼠标在页面中的坐标(e.pageX, e.pageY)
  3. 其次得到盒子在页面中的距离 ( box.offsetLeft, box.offsetTop)
  4. 用鼠标距离页面的坐标减去盒子在页面中的距离,得到 鼠标在盒子内的坐标
  5. 如果想要移动一下鼠标,就要获取最新的坐标,使用鼠标移动
var box = document.querySelector(".box");
box.addEventListener("mousemove", function (e) {var x = e.pageX - this.offsetLeft;var y = e.pageY - this.offsetTop;this.innerHTML = "x坐标是" + x + " y坐标是" + y;
});

:e.pageX、e.clientX、e.screenX、e.offsetX的区别

e.pageX,e.pageY:返回的值是相对于文档的定位,文档的左上角为(0,0),向右为正,向下为正,IE不支持(不包含上方工具栏);

e.screenX,e.screenY:返回的是相对于屏幕的坐标,浏览器上面的工具栏(包含上方工具栏);

e.clientX,e.clientY:返回的值是相对于屏幕可见区域的坐标,如果页面有滚动条,呗滚动条隐藏的那部分不进行计算,也可以说是相对于屏幕的坐标,但是不计算上方的工具栏(不包含上方工具栏);

e.offsetX,e.offsetY:返回的是元素距离带有定位的父元素的位置,如果没有定位的父元素就是对body,和e.pageX,e.pageY作用相同,但是只有IE支持(不包含上方工具栏)。 

clientWidth, clientHeight:包含width+padding,不包含border;
scrollWidth,scrollHeight: 包含width+padding,不包含border;
offsetWidth, offsetHeight: 包含border+padding+width;
style.width, style.height: 包含width,不包含border+padding


★ 案例:模态框拖拽

弹出框,我们也称为模态框。

案例需求:

  1. ​ 点击弹出层,会弹出模态框, 并且显示灰色半透明的遮挡层。
  2. ​ 点击关闭按钮,可以关闭模态框,并且同时关闭灰色半透明遮挡层。
  3. ​ 鼠标放到模态框最上面一行,可以按住鼠标拖拽模态框在页面中移动。
  4. ​ 鼠标松开,可以停止拖动模态框移动

案例分析:

  1. 点击弹出层, 模态框和遮挡层就会显示出来 display:block;
  2. 点击关闭按钮,模态框和遮挡层就会隐藏起来 display:none;
  3. 在页面中拖拽的原理:鼠标按下并且移动, 之后松开鼠标
  4. 触发事件是鼠标按下mousedown,鼠标移动mousemove 鼠标松开 mouseup
  5. 拖拽过程: 鼠标移动过程中,获得最新的值赋值给模态框的left和top值,这样模态框可以跟着鼠标走了
  6. 鼠标按下触发的事件源是最上面一行,就是 id 为 title
  7. 鼠标的坐标减去 鼠标在盒子内的坐标, 才是模态框真正的位置。
  8. 鼠标按下,我们要得到鼠标在盒子的坐标。
  9. 鼠标移动,就让模态框的坐标 设置为 :鼠标坐标 减去盒子坐标即可,注意移动事件写到按下事件里面。
  10. 鼠标松开,就停止拖拽,就是可以让鼠标移动事件解除

代码实现:

// 1. 获取元素
var login = document.querySelector(".login");
var mask = document.querySelector(".login-bg");
var link = document.querySelector("#link");
var closeBtn = document.querySelector("#closeBtn");
var title = document.querySelector("#title");
// 2. 点击弹出层这个链接 link  让mask 和login 显示出来
link.addEventListener("click", function () {mask.style.display = "block";login.style.display = "block";
});
// 3. 点击 closeBtn 就隐藏 mask 和 login
closeBtn.addEventListener("click", function () {mask.style.display = "none";login.style.display = "none";
});
// 4. 开始拖拽
// (1) 当我们鼠标按下, 就获得鼠标在盒子内的坐标
title.addEventListener("mousedown", function (e) {var x = e.pageX - login.offsetLeft;var y = e.pageY - login.offsetTop;// (2) 鼠标移动的时候,把鼠标在页面中的坐标,减去 鼠标在盒子内的坐标就是模态框的left和top值document.addEventListener("mousemove", move);function move(e) {login.style.left = e.pageX - x + "px";login.style.top = e.pageY - y + "px";}// (3) 鼠标弹起,就让鼠标移动事件移除document.addEventListener("mouseup", function () {document.removeEventListener("mousemove", move);});
});

完整代码整理:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><!-- css样式 --><style>* {margin: 0;padding: 0;box-sizing: border-box;}.login-bg {display: none;width: 100%;height: 100%;position: fixed;top: 0;left: 0;background: rgba(0, 0, 0, 0.3);}.login-header {font-size: 20px;margin: 10px 0;text-align: center;}a {text-decoration: none;color: #333;}.login {position: fixed;left: 50%;top: 50%;transform: translate(-50%, -50%);display: none;width: 480px;padding-bottom: 40px;background-color: #fff;border: 1px solid #ebebeb;box-shadow: 0px 0px 20px #ddd;z-index: 3;}.login-title {position: relative;text-align: center;padding: 20px 0 30px;font-size: 18px;cursor: move;}.close-login {position: absolute;top: -20px;right: -20px;display: block;text-align: center;line-height: 40px;width: 40px;height: 40px;border-radius: 50%;font-size: 12px;border: 1px solid #ccc;background-color: #fff;}.login-input-content {margin-left: 20px;font-size: 14px;}.login-input:nth-child(1) label {margin-left: 14px;}.login-input-content input {width: 340px;height: 30px;outline: none;border: none;border: 1px solid #ccc;margin-bottom: 30px;padding-left: 5px;}.login-button {padding-bottom: 30px;width: 260px;height: 30px;border: 1px solid #ccc;margin: 0 auto;text-align: center;line-height: 30px;}</style></head><body><!-- html结构 --><div class="login-header"><a href="javascript:;">点击,弹出登录框</a></div><div class="login"><div class="login-title">登录会员<span><a href="javascript:;" class="close-login">关闭</a></span></div><div class="login-input-content"><div class="login-input"><label for="">用户名:</label><inputtype="text"placeholder="请输入用户名"name="info[username]"id="username"class="username"/></div><div class="login-input"><label for="">登录密码:</label><inputtype="text"placeholder="请输入登录密码"name="info[password]"id="password"class="password"/></div></div><div class="login-button" id="loginBtn"><a href="javascript:;" id="login-button-submit">登录会员</a></div></div><!-- 遮盖层 --><div id="bg" class="login-bg"></div><!-- js代码 --><script>// 1. 获取元素let loginHeader = document.querySelector(".login-header");let login = document.querySelector(".login");let bg = document.getElementById("bg");let close = document.querySelector(".close-login");// 2. 点击,弹出登录框和遮盖层loginHeader.onclick = function () {login.style.display = "block";bg.style.display = "block";};// 3. 点击关闭,隐藏登录框和遮盖层close.onclick = function () {login.style.display = "none";bg.style.display = "none";};// 4. 鼠标放到模态框最上面的登录会员一行,可以按住鼠标拖拽模态框在页面中移动// 由于鼠标拖拽模态框时,鼠标的位置是不变的,所以要先获取鼠标在登录会员框中的位置// (1) 当鼠标按下,就能获得鼠标在盒子内的坐标let title = document.querySelector(".login-title");title.onmousedown = function (e) {let x = e.pageX - login.offsetLeft;let y = e.pageY - login.offsetTop;// (2) 鼠标移动的时候,把鼠标在页面中的坐标,减去鼠标在盒子内的坐标就是模态框的left和top值document.addEventListener("mousemove", move);// 由于后面会移除事件,为了方便使用所以这里给回调函数单独设置并命名为movefunction move(e) {login.style.left = e.pageX - x + "px";login.style.top = e.pageY - y + "px";}// (3) 鼠标弹起mouseup,就让鼠标移动事件移除document.addEventListener("mouseup", function () {document.removeEventListener("mousemove", move);});};</script></body>
</html>

★ 案例:仿京东放大镜

案例需求

  1. 整个案例可以分为三个功能模块
  2. 鼠标经过小图片盒子, 黄色的遮挡层 和 大图片盒子显示,离开隐藏2个盒子功能
  3. 黄色的遮挡层跟随鼠标功能。
  4. 移动黄色遮挡层,大图片跟随移动功能。

案例分析:

  1. 黄色的遮挡层跟随鼠标功能。
  2. 把鼠标坐标给遮挡层不合适。因为遮挡层坐标以父盒子为准。
  3. 首先是获得鼠标在盒子的坐标。
  4. 之后把数值给遮挡层做为left 和top值。
  5. 此时用到鼠标移动事件,但是还是在小图片盒子内移动。
  6. 发现,遮挡层位置不对,需要再减去盒子自身高度和宽度的一半。
  7. 遮挡层不能超出小图片盒子范围。
  8. 如果小于零,就把坐标设置为0
  9. 如果大于遮挡层最大的移动距离,就把坐标设置为最大的移动距离
  10. 遮挡层的最大移动距离:小图片盒子宽度 减去 遮挡层盒子宽度

代码实现:

window.addEventListener('load', function() {var preview_img = document.querySelector('.preview_img');var mask = document.querySelector('.mask');var big = document.querySelector('.big');// 1. 鼠标经过 preview_img 显示和隐藏 mask 遮挡层 和 big 大盒子preview_img.addEventListener('mouseover', function() {mask.style.display = 'block';big.style.display = 'block';})preview_img.addEventListener('mouseout', function() {mask.style.display = 'none';big.style.display = 'none';})// 2. 鼠标移动的时候,让黄色的盒子跟着鼠标来走preview_img.addEventListener('mousemove', function(e) {// (1). 先计算出鼠标在盒子内的坐标var x = e.pageX - this.offsetLeft;var y = e.pageY - this.offsetTop;// console.log(x, y);// (2) (鼠标在盒子内的坐标)-(盒子高度的一半)=(mask的最终left和top值)var maskX = x - mask.offsetWidth / 2;var maskY = y - mask.offsetHeight / 2;// (3) 如果x坐标小于0 就让他停在0的位置// 遮挡层的最大移动距离var maskMax = preview_img.offsetWidth - mask.offsetWidth;if (maskX <= 0) {maskX = 0;} else if (maskX >= maskMax) {maskX = maskMax;}if (maskY <= 0) {maskY = 0;} else if (maskY >= maskMax) {maskY = maskMax;}mask.style.left = maskX + 'px';mask.style.top = maskY + 'px';// 3. 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离// 大图var bigIMg = document.querySelector('.bigImg');// 大图片最大移动距离var bigMax = bigIMg.offsetWidth - big.offsetWidth;// 大图片的移动距离 X Yvar bigX = maskX * bigMax / maskMax;var bigY = maskY * bigMax / maskMax;bigIMg.style.left = -bigX + 'px';bigIMg.style.top = -bigY + 'px';})
})

四、番外:元素视图方法

元素视图方法概述

元素视图有三个方法,分别是getBoundingClientRect、getClientRects(忽略)和elementFromPoint(忽略)。

参考:深入理解元素视图的3个方法 - 小火柴的蓝色理想 - 博客园

getBoundingClientRect

获取dom元素相对于浏览器视口的位置集合。不同浏览器,集合中的元素略有不同,ie8及以下版本的浏览器中没有width和height属性,若出现width和height属性,则它们是偏移宽高offset。位置集合中主要包含四个属性,分别是top、right、bottom和left,其中top和left是dom元素左上顶点相对于文档的偏移坐标,bottom和right是dom元素右下顶点相对于文档的偏移坐标。该方法的返回结果不是实时的,相当于一个快照。该方法是es5添加的方法,兼容性于所有主流浏览器。

<div id="div" style="width:100px;height:200px;padding:100px;"></div>
<script>console.log(div.offsetWidth);  //"300"console.log(div.offsetHeight); //"400"console.log(div.getBoundingClientRect());
</script>

<div id="div" style="width:100px;height:100px;"></div>
<script>var box = div.getBoundingClientRect();console.log(box.width); // "100"div.style.width = "200px";console.log(box.width); // "100", getBoundingClientRect返回结果是静态的
</script>

参考:JS | JS中的getBoundingClientRect()方法详解,秒懂!- 烤地瓜的CSDN博客


● 参考资料 ●

scrollTop及offsetTop的对比_scrolltop和offsettop-CSDN博客

深入理解元素视图的3个方法-getBoundingClientRect/getClientRects/elementFromPoint - 博客园

—— JS | JavaScrip之深入理解滚动scroll大小系列属性- 烤地瓜的CSDN博客 ——

—— JS | JavaScrip之深入理解客户区尺寸client系列属性 - 烤地瓜的CSDN博客 ——

—— JS | 详解图片懒加载的6种实现方案之利用JS监听scroll滚动事件 - 烤地瓜的CSDN博客 ——

一文看懂JS中元素偏移量(offsetLeft,offsetTop,offsetWidth,offsetHeight) - 博客园

Javascript进阶篇之DOM节点 - 获取浏览器窗口可视区域大小+获取网页尺寸) - 博客园


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/452195.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

工信部绿色工厂、绿色设计产品、绿色供应链企业、绿色园区名单(2017-2022年)

我国工信部积极推动制造业的绿色转型&#xff0c;为了表彰在绿色制造领域取得显著成绩的企业和园区&#xff0c;发布了包括绿色工厂、绿色设计产品、绿色供应链企业、绿色园区在内的一系列公示名单。 2017年-2022年工信部绿色工厂、绿色设计产品、绿色供应链企业、绿色园区名单…

UDP协议讲解

预备知识&#xff1a; 端口号port&#xff1a; 我们在正常网络通信时&#xff0c;实际上是进程在互相通信。 我们所有的网络通信的行为&#xff0c;本质上都是进程间通信。 对双方而言&#xff0c;1.先保证数据能到达自己的机器 ip解决 2.找到指定的进程 端口号 ip地址用来…

Linux部署redis保姆级教程

一、版本说明 Redis版本号&#xff08;本文的版本号是6.2.12&#xff09;的第二位如果是偶数&#xff0c;代表稳定版本&#xff0c;如果是奇数&#xff0c;代表非稳定版本。 所有历史版本下载地址&#xff1a;Index of /releases/ 二、基于压缩包安装&#xff08;推荐&#xff…

【中危】Oracle TNS Listener SID 可以被猜测

一、漏洞详情 Oracle 打补丁后&#xff0c;复测出一处中危漏洞&#xff1a;Oracle TNS Listener SID 可以被猜测。 可以通过暴力猜测的方法探测出Oracle TNS Listener SID&#xff0c;探测出的SID可以用于进一步探测Oracle 数据库的口令。 建议解决办法&#xff1a; 1. 不应该使…

RISC-V笔记——RVWMO基本体

1. 前言 RISC-V使用的内存模型是RVWMO(RISC-V Weak Memory Ordering)&#xff0c;它是Release Consistency的扩展&#xff0c;因此&#xff0c;RVWMO的基本特性类似于RC模型。 2. RC模型 Release consistency(RC)的提出是基于一个观察&#xff1a;将所有同步操作用FENCE围在一…

机器学习:开启智能未来的钥匙

一、机器学习概述 机器学习作为人工智能的核心方法&#xff0c;通过分析数据中的隐藏规律&#xff0c;让计算机从中获取新的经验和知识&#xff0c;不断提升和改善自身性能&#xff0c;从而像人一样根据所学知识做出决策。 机器学习涉及概率论、统计学、微积分、代数学、算法…

Java | Leetcode Java题解之第495题提莫攻击

题目&#xff1a; 题解&#xff1a; class Solution {public int findPoisonedDuration(int[] timeSeries, int duration) {int ans 0;int expired 0;for (int i 0; i < timeSeries.length; i) {if (timeSeries[i] > expired) {ans duration;} else {ans timeSerie…

go+bootstrap实现简单的注册登录和管理

概述 使用&#xff0c;gomysql实现了用户的登录&#xff0c;注册&#xff0c;和管理的简单功能&#xff0c;不同用户根据不同权限显示不同的内容 实战要求&#xff1a; 1、用户可以注册、登录&#xff1b; 2、登录后可以查看所有的注册的用户&#xff1b; 3、管理员操作对用…

Gin框架操作指南03:HTML渲染

官方文档地址&#xff08;中文&#xff09;&#xff1a;https://gin-gonic.com/zh-cn/docs/ 注&#xff1a;本教程采用工作区机制&#xff0c;所以一个项目下载了Gin框架&#xff0c;其余项目就无需重复下载&#xff0c;想了解的读者可阅读第一节&#xff1a;Gin操作指南&#…

【Petri网导论学习笔记】Petri网导论入门学习(八) —— 1.6 系统的Petri网模型

导航 1.6 系统的Petri网模型例 1.6 化学反应例 1.7 进程的通信协议例 1.8 P/V操作例 1.9 临界段互斥问题例 1.10 生产者/消费者问题例 1.11 哲学家就餐问题 1.6 系统的Petri网模型 理论的目的在于应用&#xff0c;接下来是一些关于用Petri网标识离散事件系统的例子 这里就直接…

电能表预付费系统-标准传输规范(STS)(13)

6.3 Token data elements 令牌数据元素 6.3.1 Data elements used in tokens 使用在令牌上的数据元素 The data elements given in Table 1 3 are used in tokens in various combinations and are all encoded in binary format. 表13中给出的数据元素以各种组合用于令牌中&…

DISTINCT 去重

1. 单字段去重 以表 student_course 和 表 student 链接为例&#xff1a; SELECT * FROM student_course a INNER JOIN student b ON a.student_idb.id;查询结果如下图&#xff1a; 上图查询结果中&#xff0c;若只需要学生信息&#xff0c;则需要对结果进行去重&#xff1a;…

从零开始学PHP之helloworld

前言 每一门编程语言的第一个程序就是输出hell world&#xff08;别杠&#xff0c;杠就是你对&#xff09; 开始 上一篇讲完了开发环境的安装&#xff0c;这次讲编辑器的安装&#xff0c;顺带完成上一篇的作业&#xff08;输出hello world&#xff09; 安装PHPstorm 我用的…

分布式介绍

CAP理论 CAP理论是分布式架构中提出来的一种设计思想模型&#xff0c;全称是由Consistency、Availability、Partition Tolerance三个词组成。 C(Consistency&#xff0c;一致性):总能读到最新的写操作的结果A(Availability&#xff0c;可用性):每个请求都要在合理的时间内给出…

如何将本地 Node.js 服务部署到宝塔面板:完整的部署指南

文章简介&#xff1a; 将本地开发的 Node.js 项目部署到线上服务器是开发者常见的工作流程之一。在这篇文章中&#xff0c;我将详细介绍如何将本地的 Node.js 服务通过宝塔面板&#xff08;BT 面板&#xff09;上线。宝塔面板是一个强大的服务器管理工具&#xff0c;具有简洁的…

使用 Go 语言实现 WebSocket的核心逻辑

文章目录 WebSocket 简介时序图核心逻辑Client 结构与功能创建新客户端消息读取逻辑 (ReadPump)发送消息逻辑 (Send)客户端管理器 (ClientManager)WebSocket 处理器处理心跳与长连接 总结 本文将基于 Go 语言&#xff0c;通过使用 gorilla/websocket 库来实现一个简单的聊天应用…

教电脑“看”图片

教电脑“看”图片 计算机视觉简介 上一篇&#xff1a;《自己DIY首个人工智能模型》 序言&#xff1a;人是如何“看”图片的&#xff1f;人类感知周围世界&#xff0c;主要依赖看、听、闻、触这些感官&#xff0c;而“看”是最普遍和直观的方式。计算机视觉&#xff0c;就是对…

前端html,css 样式巩固1

想做这样 一个效果 点击图片切换 当前的选中图片 我们使用 原生的js html 来开发这个 直接粘贴代码 相信大家 都能看懂的 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" …

力扣动态规划基础版(斐波那契类型)

70. 爬楼梯https://leetcode.cn/problems/climbing-stairs/ 70.爬楼梯 方法一 动态规划 考虑转移方程和边界条件&#xff1a; f&#xff08;x&#xff09; f&#xff08;x -1&#xff09; f&#xff08;x - 2&#xff09;;f&#xff08;1&#xff09; 1&#xff1b;f&…

CNN-BiLSTM回归预测 | MATLAB实现CNN-BiLSTM卷积双向长短期记忆神经网络多输入单输出回归预测

回归预测 | MATLAB实现CNN-BiLSTM(卷积双向长短期记忆神经网络)多输入单输出 目录 回归预测 | MATLAB实现CNN-BiLSTM(卷积双向长短期记忆神经网络)多输入单输出效果一览基本介绍程序设计学习总结参考资料效果一览 基本介绍 提出一种同时考虑时间与空间因素的卷积-双向长短期记…