day-040-forty-20230401-冒泡相关事件-拖拽-放大镜
冒泡相关事件
mouseenter/mouseleave与mouseover/mouseout
-
mouseover/mouseout 有冒泡,忽略层级之间的关系
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>冒泡机制</title> <style> * { margin: 0; padding: 0; } #parent { height: 500px; width: 500px; background-color: red; } #son { height: 300px; width: 300px; background-color: blue; } </style> </head> <body> <div id="parent"> <div id="son"></div> </div> </body> </html> <script> let parent = document.getElementById("parent"); let son = document.getElementById("son"); parent.onmouseover =function(){ console.log('parent移入-onmouseover') } parent.onmouseout =function(){ console.log('parent移出-onmouseout') } son.onmouseover =function(){ console.log('son移入-onmouseover') } son.onmouseout =function(){ console.log('son移出-onmouseout') } </script>
-
mouseenter/mouseleave 没冒泡,不会忽略层级之间的关系
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>冒泡机制</title> <style> * { margin: 0; padding: 0; } #parent { height: 500px; width: 500px; background-color: red; } #son { height: 300px; width: 300px; background-color: blue; } </style> </head> <body> <div id="parent"> <div id="son"></div> </div> </body> </html> <script> let parent = document.getElementById("parent"); let son = document.getElementById("son"); parent.onmouseenter =function(){ console.log('parent移入-onmouseenter') } parent.onmouseleave =function(){ console.log('parent移出-onmouseleave') } son.onmouseenter =function(){ console.log('son移入-onmouseenter') } son.onmouseleave =function(){ console.log('son移出-onmouseleave') } </script>
事件委托
- 事件委托: 也叫事件代理,将绑定的事件委托给祖先元素,祖先元素监听事件,并利用e.target来分配给当前元素
- 原理是: 事件冒泡机制
- 事件委托的好处
-
减少事件数量,提高性能
-
预测未来元素,新添加的元素仍然可以触发该事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>2.事件委托</title> </head> <body> <ul class="ulBox"> <li>001</li> <li>002</li> <li>003</li> <p>ppp</p> <li>004</li> <li>005</li> </ul> </body> </html> <script> // let ulBox = document.querySelector(".ulBox"); // let newli1 = document.createElement("li"); // newli1.innerHTML = "aaa"; // ulBox.appendChild(newli1); // let lis = document.querySelectorAll("li"); // for (let i = 0; i < lis.length; i++) { // lis[i].onclick = function () { // console.log(i); // }; // } // let newli2 = document.createElement("li"); // newli2.innerHTML = "bbb"; // ulBox.appendChild(newli2);//这个默认没事件 let ulBox = document.querySelector(".ulBox"); let newli1 = document.createElement("li"); newli1.innerHTML = "aaa"; ulBox.appendChild(newli1); ulBox.onclick = function (e) { if (e.target.tagName === "LI") { console.log(e.target); } }; let newli2 = document.createElement("li"); newli2.innerHTML = "bbb"; ulBox.appendChild(newli2); //这个依旧有事件 </script>
-
避免内存外泄,在低版本的IE中,防止删除元素而没有移除事件而造成的内存溢出
-
点击案例
点击按钮会打印出内部data-type属性的值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3.点击事件</title>
</head>
<body>
<div class="box">
<button data-type="+">+</button>
<button data-type="-">-</button>
<button data-type="*">*</button>
<button data-type="/">/</button>
</div>
</body>
</html>
<script>
let box = document.querySelector('.box')
box.onclick=function(e){
if(e.target.tagName==='BUTTON'){
const theDataType = e.target.getAttribute('data-type')
console.log(theDataType)
}
}
</script>
拖拽
简版
- 对小盒子绑定一个mousedown鼠标点击事件
- 移入到小盒子里面按下时,通过e.offsetX与e.offsetY获取鼠标相对于当前盒子中的初始坐标
- 对window绑定一个mouseup事件。在整个页面,按着鼠标移动,抬起的瞬间求结束坐标
- mouseup事件事件中,通过e.pageX与e.pageY拿到鼠标相对于文档的终点坐标,之后修改小盒子的top与left。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>拖拽</title>
<style>
.box {
height: 100px;
width: 100px;
background-color: skyblue;
position: absolute;
top: 0px;
left: 0px;
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>
<script>
let box = document.querySelector(".box");
let startX=0, startY=0, endX=0, endY=0;
box.onmousedown = function (e) {
//移入到小盒子里面按下获取初始坐标
startX = e.offsetX;
startY = e.offsetY;
console.log(startX, startY);
//在整个页面,按着鼠标移动,抬起的瞬间求结束坐标
window.addEventListener("mouseup", move);
};
const move = function move(e) {
//结束坐标
endX = e.clientX;
endY = e.clientY;
//盒子最后的位置 结束坐标-初始坐标
box.style.left = endX - startX + "px";
box.style.top = endY - startY + "px";
//抬起结束后,要移除抬起事件,否则盒子就一直还跟着鼠标
window.removeEventListener("mouseup", move);
};
</script>
有限制版
-
鼠标在当前元素中的坐标替换
-
e.offsetX可以用e.clientX-box.offsetLeft代替
-
e.offsetY可以用e.clientY-box.offsetTop
box.onmousedown=function(e){//进入盒子按下的一瞬间,获取初始坐标 startX=e.offsetX; startY=e.offsetY; //再整个文档中移动鼠标,抬起瞬间获取结束坐标 } //等价于 box.onmousedown = function (e) { startX = e.clientX - box.offsetLeft; startY = e.clientY - box.offsetTop; };
-
-
在小盒子点击后,添加了window的mousemove事件。文章来源:https://www.toymoban.com/news/detail-402685.html
- 用于让小盒子在鼠标没松开时,一直跟随着鼠标移动。
- 同时对于小盒子的移动范围进行了限制,让其只能在视口中出现
- 视口用的客户端宽度document.documentElement.clientWidth减去当前小盒子宽度box.offsetWidth来计算得出的
- 同时对于小盒子的移动范围进行了限制,让其只能在视口中出现
- 用于让小盒子在鼠标没松开时,一直跟随着鼠标移动。
-
在小盒子移动后,依旧使用window的mouseup事件移除mousemove对应函数及mouseup对应事件函数文章来源地址https://www.toymoban.com/news/detail-402685.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>拖拽3</title>
<style>
.box {
height: 100px;
width: 100px;
background-color: skyblue;
position: absolute;
top: 0px;
left: 0px;
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>
<script>
let box = document.querySelector(".box");
let startX, startY;
box.onmousedown = function (e) {
// //移入到小盒子里面按下获取初始坐标
// startX = e.offsetX;
// startY = e.offsetY;
// 假设无法通过offsetX/offsetY获取初始坐标
startX = e.clientX - this.offsetLeft;
startY = e.clientY - this.offsetTop;
console.log(startX, startY);
//在整个页面,按着鼠标移动,抬起的瞬间求结束坐标
window.addEventListener("mousemove", move);
window.addEventListener("mouseup", up);
};
const move = function move(e) {
let html = document.documentElement || document.body;
let maxWidth = html.clientWidth - box.offsetWidth;
let maxHeight = html.clientHeight - box.offsetHeight;
//结束坐标
let endX = e.clientX;
let endY = e.clientY;
let resX = endX - startX;
resX = Math.max(0, resX);
resX = Math.min(maxWidth, resX);
let resY = endY - startY;
resY = Math.max(0, resY);
resY = Math.min(maxHeight, resY);
//盒子最后的位置 结束坐标-初始坐标
box.style.left = resX + "px";
box.style.top = resY + "px";
};
const up = function up(e) {
window.removeEventListener("mousemove", move);
window.removeEventListener("mouseup", up);
};
</script>
多盒拖拽
- 每个盒子都有自己的起始坐标,结束坐标,范围值,多个盒子之间互不影响…
- 思路是:
- 给每个盒子添加 私有的属性值
- 在mousedown事件中使用this来给每个盒子添加需要用到的私有属性值
- 如果是事件,也要修改this指向
- 修改this指向,可以使用函数原型__proto__上的call()、apply()、bind()。这里主要使用bind()。
- 如果是事件,也要修改this指向
- 在mousedown事件中使用this来给每个盒子添加需要用到的私有属性值
- 在小盒子点击后,添加了window的mousemove事件,用于让小盒子跟随鼠标移动。
- 在小盒子移动后,依旧使用window的mouseup事件移除mousemove对应函数及mouseup对应事件函数。
- 给每个盒子添加 私有的属性值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#box {
height: 100px;
width: 100px;
background-color: pink;
position: fixed;
left: 0;
top: 0;
}
#boxtwo {
height: 200px;
width: 200px;
background-color: aqua;
position: fixed;
left: 0;
top: 200px;
}
</style>
</head>
<body>
<div id="box"></div>
<div id="boxtwo"></div>
</body>
</html>
<script>
let html = document.documentElement || document.body;
//每个盒子都有自己的起始坐标,结束坐标,范围值...
//给每个盒子添加 私有的属性值
function down(e) {
this.startX = e.offsetX;
this.startY = e.offsetY;
//修改this指向 call apply bind
this._move = move.bind(this);
this._up = up.bind(this);
window.addEventListener("mousemove", this._move);
window.addEventListener("mouseup", this._up);
}
function move(e) {
//console.log(this);//window
//console.log(e.target);
this.endX = e.clientX;
this.endY = e.clientY;
this.resultX = this.endX - this.startX;
this.resultY = this.endY - this.startY;
this.maxW = html.clientWidth - this.offsetWidth;
this.maxH = html.clientHeight - this.offsetHeight;
this.resultX = this.resultX >= this.maxW ? this.maxW : this.resultX <= 0 ? 0 : this.resultX;
this.resultY = this.resultY >= this.maxH ? this.maxH : this.resultY <= 0 ? 0 : this.resultY;
this.style.left = this.resultX + "px";
this.style.top = this.resultY + "px";
}
function up() {
window.removeEventListener("mousemove", this._move);
window.removeEventListener("mouseup", this._up);
}
box.addEventListener("mousedown", down);
boxtwo.addEventListener("mousedown", down);
</script>
放大镜
放大镜简版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<style>
.box {
width: 700px;
margin: 50px auto;
height: 400px;
display: flex;
}
.leftbox {
width: 300px;
height: 300px;
position: relative;
}
.leftbox img {
width: 300px;
height: 300px;
}
.leftbox .mark {
display: none;
width: 100px;
height: 100px;
background-color: red;
opacity: 0.5;
position: absolute;
left: 0;
top: 0;
}
.rightbox {
display: none;
width: 400px;
height: 400px;
overflow: hidden;
position: relative;
}
.rightbox img {
/* width:1200px;
height:1200px; */
position: absolute;
left: 0;
top: 0;
}
</style>
</head>
<body>
<div class="box">
<div class="leftbox">
<img src="images/1.jpg" alt="" />
<div class="mark"></div>
</div>
<div class="rightbox">
<img src="images/2.jpg" alt="" />
</div>
</div>
</body>
</html>
<script>
(function () {
//1.求出大图片的高度和宽度
let leftbox = document.querySelector(".leftbox"); //左侧盒子
let mark = document.querySelector(".mark"); //遮罩层
let rightbox = document.querySelector(".rightbox"); //右侧盒子
let bigimg = document.querySelector(".rightbox img"); //大图片
function getwh(ele, attr) {
return parseFloat(window.getComputedStyle(ele)[attr]);
}
//左侧盒子 高度和宽度
let leftboxW = leftbox.offsetWidth;
let leftboxH = leftbox.offsetHeight;
//右侧盒子 高度和宽度
//隐藏的盒子无法通过offsetWidth/offsetHeight 求值,默认为0
let rightboxW = getwh(rightbox, "width");
let rightboxH = getwh(rightbox, "height");
//遮罩层 高度和宽度
let markW = getwh(mark, "width");
let markH = getwh(mark, "height");
//设置大图片的高度和宽度
bigimg.style.width = (rightboxW / markW) * leftboxW + "px";
bigimg.style.height = (rightboxH / markH) * leftboxH + "px";
//移入盒子,mark和右侧盒子显示
leftbox.onmouseenter = function () {
mark.style.display = "block";
rightbox.style.display = "block";
};
//移出盒子,mark和右侧盒子隐藏
leftbox.onmouseleave = function () {
mark.style.display = "none";
rightbox.style.display = "";
};
//鼠标在左侧小盒移动,mark也移动
leftbox.onmousemove = function (e) {
let cx = e.clientX;
let cy = e.clientY;
let maxW = leftboxW - markW;
let maxH = leftboxH - markH;
let resX = cx - markW / 2 - leftbox.offsetLeft;
let resY = cy - markH / 2 - leftbox.offsetTop;
resX = resX < 0 ? 0 : resX > maxW ? maxW : resX;
resY = resY < 0 ? 0 : resY > maxH ? maxH : resY;
//移动红色遮罩层
mark.style.left = resX + "px";
mark.style.top = resY + "px";
//移动大图片---按照比率
bigimg.style.left = "-" + (rightboxW / markW) * resX + "px";
bigimg.style.top = "-" + (rightboxH / markH) * resY + "px";
};
})();
</script>
个人优化版
- 用了css变量,在容器上放置了相对定位对放大镜用了绝对定位。
- 进一步优化,.left-box为容器,大图片盒子放在.left-box里,去除.box这一层级。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<style>
.box {
--min-image-width: 300px;
--min-image-height: 300px;
--mark-width: 100px;
--mark-height: 100px;
--scale: 4;
margin: 250px;
display: flex;
position: relative;
}
.box .left-box {
width: var(--min-image-width);
height: var(--min-image-height);
position: relative;
}
.box .left-box img {
width: var(--min-image-width);
height: var(--min-image-height);
}
.box .left-box .mark {
display: none;
width: var(--mark-width);
height: var(--mark-height);
background-color: red;
opacity: 50%;
position: absolute;
left: 0px;
top: 0px;
}
.box .right-box {
display: none;
width: calc(var(--mark-width) * var(--scale));
height: calc(var(--mark-height) * var(--scale));
overflow: hidden;
position: absolute;
left: var(--min-image-width);
top: 0px;
}
.box .right-box img {
width: calc(var(--min-image-width) * var(--scale));
height: calc(var(--min-image-height) * var(--scale));
position: absolute;
left: 0px;
top: 0px;
}
</style>
</head>
<body>
<div class="box">
<div class="left-box">
<!-- <img src="./images/1.jpg" alt="小图"> -->
<img
src="https://cdn.cnbj0.fds.api.mi-img.com/b2c-shopapi-pms/pms_1672220519.85821941.png"
alt="大图"
/>
<div class="mark"></div>
</div>
<div class="right-box">
<img
src="https://cdn.cnbj0.fds.api.mi-img.com/b2c-shopapi-pms/pms_1672220519.85821941.png"
alt="大图"
/>
</div>
</div>
</body>
<script>
(function () {
//1. 未出图片的高度和宽度
let box = document.querySelector(".box");
let leftBox = box.querySelector(".left-box");
let mark = leftBox.querySelector(".mark");
let rightBox = box.querySelector(".right-box");
let bigImage = rightBox.querySelector("img");
//左侧盒子 高度和宽度
let leftBoxWidth = leftBox.offsetWidth;
let leftBoxHeight = leftBox.offsetHeight;
const getWH = function getWH(ele, attr) {
// console.log(window.getComputedStyle(ele)[attr]);
return parseInt(window.getComputedStyle(ele)[attr]) || 0;
};
//右侧盒子 高度和宽度
// 隐藏的盒子无法通过offsetWidth/offsetHeight求值,默认为0
let rightBoxWidth = getWH(rightBox, "width");
let rightBoxHeight = getWH(rightBox, "height");
let markWidth = getWH(mark, "width");
let markHeight = getWH(mark, "height");
let bigImageWidth = (rightBoxWidth / markWidth) * leftBoxWidth;
let bigImageHeight = (rightBoxHeight / markHeight) * leftBoxHeight;
// console.log(bigImageWidth, bigImageHeight);
bigImage.style.width = `${bigImageWidth}px`;
bigImage.style.height = `${bigImageHeight}px`;
leftBox.onmouseenter = function () {
mark.style.display = "block";
rightBox.style.display = "block";
};
leftBox.onmouseleave = function () {
mark.style.display = "none";
rightBox.style.display = "none";
};
leftBox.onmousemove = function (e) {
let cx = e.clientX;
let cy = e.clientY;
let maxWidth = leftBoxWidth - markWidth;
let maxHeight = leftBoxHeight - markHeight;
let resX = cx - markWidth / 2 - leftBox.getBoundingClientRect().left;
let resY = cy - markHeight / 2 - leftBox.getBoundingClientRect().top;
console.log(cx, cy, resX, resY, maxWidth, maxHeight);
resX = resX < 0 ? 0 : resX > maxWidth ? maxWidth : resX;
resY = resY < 0 ? 0 : resY > maxHeight ? maxHeight : resY;
mark.style.left = `${resX}px`;
mark.style.top = `${resY}px`;
bigImage.style.left = `-${resX * (rightBoxWidth / markWidth)}px`;
bigImage.style.top = `-${resY * (rightBoxHeight / markHeight)}px`;
};
})();
</script>
</html>
进阶参考
到了这里,关于20230401----重返学习-冒泡相关事件-拖拽-放大镜的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!