DOM事件
一、事件
事件:浏览器赋予元素的默认行为,可以理解为事件是天生具备的。不论是否为其绑定方法,当某些行为触发的时候,相关的事件都会被触发执行。
事件绑定:给元素默认的事件行为绑定方法,这样在行为触发的时候才会执行绑定的方法。
document.body.onclick=function (){};
- 大部分人:给body绑定一个点击事件
- 标准:给body的点击事件行为绑定方法
事件行为还有很多,这里我们暂时列举这些;更多的内容可以参考 MDN,事件参考; 或者可以查看元素的属性(属性中onxxx就是元素拥有的事件行为)
1.浏览器常用事件
1.1鼠标事件
元素.onclick=function(){}
单击(移动端:300ms内没触发第二次,所以click在移动端有300ms延迟);点击(PC端)元素.oncontextmenu=function(){}
右键点击元素.ondblclick=function(){}
双击,大约300ms内连续点击两次元素.onmouseenter=function(){}
鼠标移入,进入子节点不会触发这个事件元素.onmouseleave=function(){}
鼠标离开,进入子节点不会触发这个事件元素.onmouseover=function(){}
鼠标滑入,进入子节点会触发这个事件元素.onmouseout=function(){}
鼠标滑出,进入子节点会触发这个事件元素.onmousemove=function(){}
鼠标滑动,只要鼠标动就会触发元素.onmousedown=function(){}
鼠标按下 鼠标的左右和滚轮按下就会触发元素.onmouseup=function(){}
鼠标抬起元素.onwheel=function(){}
滚轮滚动
思考:如何让按钮单击和双击做不同的事情
let btn=document.querySelector('.two');
btn.onclick=function(){
// 鼠标点击第一次的时候,先不着急执行单击函数 等一会 看是否点击了第二下
// 如果点击了第二下,就自动触发了双击,这样就不执行对应的代码了
clearTimeout(this.timer);
this.dbl=false;
this.timer=setTimeout(() => {
if(this.dbl)return;
console.log('单击'); //单击业务
}, 300);
};
btn.ondblclick=function(){
this.dbl=true;
console.log('双击');
};
思考:mouseover和mouseenter的区别
mouseover和mouseout是从视觉上看进入离开
mouseover和mouseout是忽略元素之间的层级关系,只看鼠标在哪个元素显示的范围上,存在事件冒泡
阻止事件冒泡的话:e.stopPropagation();
mouseenter和mouseleave是从层级上来进入和离开
mouseenter和mouseleave会受到元素之间的层级关系,默认阻止了事件冒泡机制
1.2键盘事件
元素.onkeydown=function(){}
键盘按下,所有键都行元素.onkeyup=function(){}
键盘抬起元素.onkeypress=function(){}
长按(有些键获取不到,如:Shift/Fn/CapsLock等)- 可以绑定键盘事件:
- input
- textarea
- window
- document
- document.body
- document.documentElement
1.3表单事件
input.onfocus=function(){}
获取光标时触发input.onblur=function(){}
失去焦点时触发input.onchange=function(){}
内容改变并且失焦input.oninput=function(){}
只要内容改变就会触发submit
表单提交(前提:表单元素都包含在form中,并且点击的按钮是 submit)reset
表单重置select
下拉框内容选中
this.value代表输入的内容
1.4系统常用事件
window.onload=function(){}
页面全部加载完window.onresize=function(){}
可视窗口发生改变时document.body.onscroll=function(){}
滚动条监听window.onscroll=function(){}
- $(document).ready() jQuery里的ready事件,原生没有
ready和load区别:ready是页面DOM结构加载完成就会触发,比load触发早
更多具体区别在Jquery 中有详细介绍
2.移动端常用事件
2.1单手指事件 Touch Event
元素.ontouchstart=function(){}
触碰到元素触发【手指碰到屏幕】元素.ontouchend=function(){}
离开元素触发【手指离开屏幕】元素.ontouchmove=function(){}
手指移动元素.onclick=function(){}
单击大约300ms延迟 【在移动端一般不用】元素.ontouchcancel
:操作取消(一般应用于非正常状态下操作结束)
2.2多手指事件模型 Gesture
- ongesturestart:手指碰到屏幕(手指按下)
- ongesturechange / ongestureupdate:手指在屏幕上移动
- ongestureend:手指离开屏幕(手指松开)
- ongesturecancel:操作取消(一般应用于非正常状态下操作结束)
3.其他事件
3.1 css3动画事件(了解)
transitionend
transition动画结束transitionstart
transition动画开始transitionrun
transition动画运行中
更多相关内容在transform animation可以查看
3.2音视频事件
使用范围:音频、视频
- canplay:可以播放(资源没有加载完,播放中可能会卡顿)
- canplaythrough:可以播放(资源已经加载完,播放中不会卡顿)
- play:开始播放
- playing:播放中
- pause:暂停播放
事件行为还有很多,这里我们暂时列举这些;更多的内容可以参考 MDN,事件参考; 或者可以查看元素的属性(属性中onxxx就是元素拥有的事件行为)
二、事件对象
1、定义
- 给元素的事件行为绑定方法,当事件行为触发方法会被执行,不仅被执行,而且还会把当前操作的相关信息传递给这个函数 =>传递过来相关信息就叫做事件对象
事件对象是由事件当前本身产生的,和执行什么函数没有关系
2、原理
事件对象和函数以及给谁绑定的事件没啥必然关系,它存储的是当前本次操作的相关信息,操作一次只能有一份信息,所以在哪个方法中获取的信息都是一样的;第二次操作,存储的信息会把上一次操作存储的信息替换掉...;
-
每一次事件触发,浏览器都会这样处理一下
- 1.捕获到当前操作的行为(把操作信息获取到),通过创建MouseEvent等类的实例,得到事件对象EV
- 2.通知所有绑定的方法(符合执行条件的)开始执行,并且把EV当做实参传递给每个方法,所以在每个方法中得到的事件对象其实是一个
- 3.后面再重新触发这个事件行为,会重新获取本次操作的信息,用新的信息替换老的信息,然后继续之前的步骤...
3、事件对象类型
1)鼠标事件对象
如果是鼠标操作,获取的是MouseEvent类的实例(这个实例就是 =>鼠标事件对象)
var box = document.getElementsByClassName('box')[0];
box.onclick = function (e) {
//e 我们一般称为事件对象 是浏览器默认传给这个函数的
//里面存储了事件的信息
e = e || window.event; //为了兼容低版本IE浏览器
target = e.target || e.srcElement;//兼容低版本IE浏览器
console.log(e);
}
-
原型链
- 鼠标事件对象 -> MouseEvent.prototype -> UIEvent.prototype -> Event.prototype -> Object.prototype
-
常用属性
- clientX/clientY:当前鼠标触发点距离当前窗口左上角的X/Y轴坐标
- pageX/pageY:触发点距离当前页面左上角的X/Y轴坐标
小方块案例
固定速度移动
<!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>
* {
padding: 0;
margin: 0;
}
#box {
width: 100px;
height: 100px;
background: lightcoral;
position: relative;
left: 0;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
<script>
let box = document.querySelector('#box'),
l = 0,
max = document.documentElement.clientWidth - box.offsetWidth,
speed = 20;
// max 是移动的路程,也是left的最大值
// speed是控制速度
/*
// setInterval写法
box.onclick = function () {
let timer = setInterval(() => {
l += speed;
if (l > max) {
l = max;
clearInterval(timer);
}
box.style.left = l + 'px';
}, 20);
}; */
// setTimeout 递归写法
/* box.onclick = function () {
function fn() {
setTimeout(() => {
// let l2 = box.getBoundingClientRect().left;
// getBoundingClientRect需要放在定时器里面,每次获取最新的值
// l每次+=的数就是速度
l += speed;
if (l > max) {
l = max;
box.style.left = l + 'px';
return;
}
box.style.left = l + 'px';
fn();
}, 30);
}
fn();
}; */
// 页面渲染完成会调用一次回调函数
// 常用每帧页面重新渲染
// 比setTimeout性能更好,专门用来写动画
// requestAnimationFrame(()=>{
// console.log(123);
// })
box.onclick = function () {
function fn() {
requestAnimationFrame(() => {
l += speed;
if (l > max) {
l = max;
box.style.left = l + 'px';
return;
}
box.style.left = l + 'px';
fn();
});
}
fn();
};
</script>
固定时间移动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=
, initial-scale=1.0"
/>
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
#box {
width: 100px;
height: 100px;
background: lightcoral;
position: relative;
left: 0;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
<script>
/*
固定速度移动,路程越长时间越长
固定时间移动,路程越长速度越快,需要求速度
*/
let box = document.querySelector('#box'),
time = 1000, //是固定的时间
max = document.documentElement.clientWidth - box.offsetWidth;
// max = 2000;
// 10ms移动一次,一共走1000ms,那么走了1000/10次
// 路程是max,一共走了1000/10次,也就是说每次走max/(1000/10)距离
let speed = max / (time / 10), //速度,每次走多远
l = 0;
box.onclick = function () {
let timer = setInterval(() => {
l += speed;
if (l > max) {
l = max;
clearInterval(timer);
}
box.style.left = l + 'px';
}, 10);
};
</script>
2)键盘事件对象
如果是键盘操作,获取的是KeyboardEvent类的实例 =>键盘事件对象
- 常用属性
- code & key:存储的都是按键,code更细致
- keyCode & which:存储的是键盘按键对应的码值
- 键盘常用码值
- 方向键:37 38 39 40 =>左上右下
- 空格SPACE:32
- 回车ENTER:13
- 回退BACK:8
- 删除DEL:46
- SHIFT:16
- CTRL:17
- ALT:18
window键盘码值:
拖拽案例
<!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>
#box {
width: 50px;
height: 50px;
background: pink;
border-radius: 50%;
position: fixed;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
<script>
let box = document.getElementById('box');
box.onmousedown = function (e) {
// 鼠标按下时,记录鼠标按下的位置
box.beginX = e.pageX;
box.beginY = e.pageY;
window.addEventListener('mouseup', move);
// 鼠标抬起绑定在box上会出现鼠标丢失,所以要绑定在window上
};
function move(e) {
console.log(123);
let x = e.pageX - box.beginX,
y = e.pageY - box.beginY;
// x,y就是鼠标移动的距离
// 元素的初始位置
let pos = box.getBoundingClientRect();
// pos.top是元素初始top;再加上y就是结束的位置
console.log(pos);
box.style.top = pos.top + y + 'px';
box.style.left = pos.left + x + 'px';
// 一直给window绑定鼠标抬起事件,在页面上随便点击也会监听到,所以在拖拽结束后,就把move移除掉
window.removeEventListener('mouseup', move);
}
</script>
推箱子案例
<!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>
#box {
position: fixed;
left: 0px;
top: 0;
width: 100px;
height: 100px;
background: lightcoral;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
<script>
let box = document.querySelector('#box');
// keypress 有一些系统键监听不到
// keydown更好用
// input textarea window document.documentElement document.body可以绑定键盘事件
let t = 0,
l = 0;
window.onkeydown = function (e) {
// e.key e.keyCode e.code都表示按下的键盘信息
// e.key e.code是英文键盘字符 e.keyCode是键盘数字
// console.log(e.key, e.keyCode, e.code);
switch (e.keyCode) {
case 37:
// 左箭头
// console.log(getComputedStyle(box).left);是个带单位的字符串
l -= 5;
box.style.left = l + 'px';
break;
case 38:
// 上箭头
t -= 5;
box.style.top = t + 'px';
break;
case 39:
// 右箭头
l+=5;
box.style.left = l + 'px';
break;
case 40:
// 下箭头
t += 5;
box.style.top = t + 'px';
break;
}
};
</script>
3)手指事件对象
changedTouches / targetTouches / touches都是记录手指信息的,平时用的多的是changedTouches
手指按下、移动、离开屏幕 changedTouches都存储了对应的手指信息,哪怕离开屏幕后,存储的也是最后一次手指在屏幕 中的信息;而 touches在手指离开屏幕后,就没有任何的信息了;=>获取的结果都是一个 TouchList集合,记录每一根手指的信息;
4、事件对象event的属性
除了上面,只有鼠标和键盘中有的属性外,还有一些公共的所有事件对象都有的属性
- type:触发事件的类型
- target:事件源(操作的是哪个元素,哪个元素就是事件源)
- 在不兼容的浏览器中可以使用srcElement获取,也代表的是事件源
- preventDefault():用来阻止默认行为的方法
- 不兼容的浏览器中用ev.returnValue=false也可以阻止默认行为
- stopPropagation():阻止冒泡传播
- 不兼容的浏览器中用ev.cancelBubble=true也可以阻止冒泡传播
需求:当用户输入完成,敲回车键的时候 alert用户输入的内容
let ipt=document.querySelector('#inp');
ipt.onkeydown=function(e){
if(e.keyCode==13){
alert(e.target.value);
};
};
三、阻止默认事件
a标签、onkeydown、ontouchmove 、右键菜单 有默认事件
-
一般优先执行绑定的函数,再去执行默认行为
-
onscroll touchmove 先执行默认
-
e.preventDefault();//阻止默认事件
-
e.returnValue=false;
-
return false
//页面标签<a href="http://www.baidu.com" target="_blank">点击百度</a>
//=> A标签的默认行为
// +页面跳转
// +锚点定位(定位到当前页面指定的ID的盒子里,URL地址会加入HASH值)
// 第一种直接在 <a href="javascript:;" target="_blank">点击百度</a>
let a=document.querySelector('#box a');
//点击A标签,先触发其点击事件行为 然后才是默认跳转
a.onclick=function(e){
// 阻止默认事件
//return false
e.preventDefault();
//e.returnValue=false;//兼容IE
}
需求:点击右键不出现系统菜单,出现自定义菜单
<!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 {
position: relative;
left: 0px;
top: 0px;
width: 100px;
height: 100px;
background: lightcoral;
transition: all ease;
}
.menu {
position: fixed;
display: none;
width: 50px;
height: 200px;
background-color: lightcoral;
}
ul {
list-style: none;
text-align: center;
}
</style>
</head>
<body>
<div id="box">
<div class="menu">
<ul>
<li>确定</li>
<li>打印</li>
</ul>
</div>
</div>
<script>
let menuDiv = document.querySelector('#box .menu');
console.log(menuDiv);
window.oncontextmenu = function (e) {
e.preventDefault();
let left = e.clientX,
top = e.clientY;
// 拿到鼠标的点击位置 把点击的位置信息给menuDiv
menuDiv.style.left = left + 'px';
menuDiv.style.top = top + 'px';
menuDiv.style.display = 'block';
};
window.onclick = function () {
menuDiv.style.display = 'none';
};
</script>
</body>
</html>
四、事件传播机制
1.事件传播
先捕获 再处理 再冒泡
Event.prototype:Event 原型上记录了冒泡传播的顺序 啥都不做是NONE 0
-
1、捕获阶段:=>CAPTURING_PHASE:1
- 会从最外层元素一层一层往里找到该元素,这个过程叫捕获
- 采集冒泡阶段的传播路径 ev.path[传播路径]
- 在IE低版本和欧朋低版本浏览器里没有捕获阶段
- element.addEventListener(event, function, true);//第三个参数 就可以实现事件捕获
- 会从最外层元素一层一层往里找到该元素,这个过程叫捕获
-
2、目标阶段:=>AT_TARGET:2
- 【目标处理阶段】把事件源的相关事件行为触发 如果为其绑定过方法,方法会执行
-
3、冒泡阶段:=>BUBBLING_PHASE:3
- 从里到外,把当前事件源所有的祖先元素的相关事件行为都依次触发
- element.addEventListener(event, function, false);//第三个参数为false或者为空的时候,代表在冒泡阶段绑定
2.阻止冒泡传播
e.stopPropagation();
-
不兼容的浏览器中用ev.cancelBubble=true也可以阻止默认行为
IE用btn.attachEvent('onclick',fn1),btn.detachEvent('onclick',fn1),绑定/删除二级事件,并且都只能在冒泡阶段触发
<!-- CSS 代码 -->
<style>
#box2{
width: 500px;
height: 500px;
background:lightskyblue;
}
#outer,#inner{
margin: auto;
}
#outer{
width: 300px;
height: 300px;
background-color: lightcoral;
}
#inner{
width: 100px;
height: 100px;
background-color: yellowgreen;
}
</style>
<!-- HTML 代码 -->
<div id="box2">
box2
<div id="outer">
outer
<div id="inner">
inner
</div>
</div>
</div>
<!-- JS 代码 -->
let box = document.querySelector('#box2'),
outer = document.querySelector('#outer'),
inner = document.querySelector('#inner');
box.onclick = function () {
console.log('box');
};
outer.onclick = function () {
console.log('outer');
};
inner.onclick = function (e) {
console.log('inner');
e.stopPropagation(); //阻止冒泡
};
五、事件委托[事件代理]
-
通过点击子级元素来触发父级元素的事件【因为事件的冒泡机制】
-
利用事件源
e.target||e.srcElment
来获取点击的元素 -
本来是要绑定给每一个子元素事件,我们可以绑定到父元素
- 通过事件的冒泡结合 e.target 实现了要的效果,避免了给每个一个元素绑定点击事件
- 每个元素绑定事件,多了话 利用给父元素绑定事件 比每个元素绑定事件性能高
应用
1、场景一
如果一个容器中很多元素都要在触发某一事件的时候做一些事情的时候,
-
原始方案:给元素每一个都单独进行事件绑定
-
基于事件委托:
- 我们只需要给当前容器的这个事件行为绑定方法,
- 这样不论是触发后代中哪一个元素的相关事件行为,由于冒泡传播机制,当前容器绑定的方法也都要被触发执行
- 想知道点击的是谁(根据是谁做不同的事情),只需要基于事件对象中的ev.target事件源获取即可
2、场景二
数据是动态绑定渲染的,要给每一条数据绑定事件行为时,选用事件委托,就可以实现动态点击的处理了,不用在逐一获取绑定了
3、场景三
除某某事件源以外的其它事件源,操作的时候统一做某事的,基本上都要基于事件委托解决
4、优点
-
基于事件委托实现,整体性能要比一个个的绑定方法高出50%左右
-
如果多元素触发,业务逻辑属于一体的,基于事件委托来处理更加好
-
某些业务场景只能基于事件委托处理
<!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>
</head>
<body>
<input type="number" value="20" disabled>
<div id="box">
<button data-type="+">+</button>
<button data-type="-">-</button>
<button data-type="+">+</button>
<button data-type="-">-</button>
<button data-type="+">+</button>
</div>
<script>
let box=document.querySelector('#box'),
input=document.querySelector('input');
//点击父盒子绑定事件
box.onclick=function(e){
//e.target就是点击的元素
//e.target.tagName 鼠标点击的dom的标签
if(e.target.tagName==='BUTTON'){
let type=e.target.getAttribute('data-type');
if(type==='+'){
console.log(input.value);
input.value++;
}else{
input.value--;
}
};
};
</script>
</body>
</html>
六、DOM0 和 DOM2 事件绑定
1、DOM0 事件绑定
-
语法:元素.on事件类型[onclick]=function(){}
-
原理:基于给元素的私有属性赋值,当条件达到触发私有属性方法
- 1.如果元素没有某个事件的私有属性,就不能基于这个方法绑定0级事件
- 2.只能绑定一个方法,如果多个绑定,只有最后一个绑定上
- 3.都是冒泡阶段触发
box.onclick = function () {
console.log(1);
}
box.onclick = function () {
console.log(2);
}
只输出后面的:
- 移除
box.onclick = function () {
console.log(2);
//=>移除事件绑定:DOM0直接赋值为null即可
box.onclick = null;
}
2、DOM2 事件绑定
- 语法
- 元素.addEventListener(事件类型,fn,true/false)
- true/false 可以省略,默认是false冒泡阶段; true代表的是捕获阶段
- IE6~8中:元素.attachEvent('on事件行为',function(){})
- 元素.addEventListener(事件类型,fn,true/false)
- 原理
- 基于原型链查找机制,找到EventTarget.prototype上的方法并且执行,此方法执行,会把给当前元素某个事件行为绑定的所有方法,存放到浏览器默认的事件池中(绑定几个方法,会向事件池存储几个);
- 当事件行为触发,会把事件池中存储的对应方法,依次按照顺序执行 “给当前元素某一个事件行为绑定多个不同方法”
事件池特点
- 基于addEventListener向事件池增加方法,存在去重的机制 “同一个元素,同一个事件类型,在事件池中只能存储一遍这个方法,不能重复存储”
- 1.只要浏览器中有这个事件,都可以通过2级事件来绑定,如transitionend,DOMContentLoaded(jquery中的ready事件就是监听这个事件实现的)
- 2.同一个事件可以绑定多个方法,触发顺序,先绑定的先触发
- 3.可以人为规定在捕获或冒泡阶段触发,默认是在冒泡阶段
function fn(){
console.log(1);
};
function fn2(){
console.log(2)
};
box.addEventListener('click',fn);
box.addEventListener('click',fn2);
两个都能输出
- 绑定注意点:DOM2事件绑定的时候,我们一般都采用实名函数
- 目的:这样可以基于实名函数去移除事件绑定
- 语法:box.addEventListener('click', fn, false);
function fn1(){ console.log(1); }
function fn2(){ console.log(2); }
function fn3(){ console.log(3); }
box.addEventListener('click', fn2, false);
box.addEventListener('click', fn3, false);
box.addEventListener('click', fn1, false);
//输出结果 2 3 1 先存先执行
//=>基于addEventListener向事件池增加方法,存在去重的机制 “同一个元素,同一个事件类型,在事件池中只能存储一遍这个方法,不能重复存储”
box.addEventListener('click', fn1, false); // 所以此步跳过,不再存储
box.addEventListener('mouseover', fn1, false); //增加
- 移除绑定:从事件池中移除,所以需要指定好事件类型、方法等信息(要和绑定的时候一样才可以移除)
- 语法:box.removeEventListener('click', fn, false)
function fn() {
console.log(1);
//=>移除事件绑定:从事件池中移除,所以需要指定好事件类型、方法等信息(要和绑定的时候一样才可以移除)
box.removeEventListener('click', fn, false);
}
box.addEventListener('click', fn, false);
3、特别注意的几点
- 1)DOM0和DOM2可以混在一起用:执行的顺序以绑定的顺序为主
box.addEventListener('click', function () {
console.log('哔咔哔咔~~');
});
box.onclick = function () {
console.log('哇咔咔~~');
}
box.addEventListener('click', function () {
console.log('call~~');
});
- 2)DOM0 比 DOM2 快
- 3)DOM0中能做事件绑定的事件行为,DOM2都支持;DOM2里面一些事件,DOM0不一定能处理绑定,例如:transitionend、DOMContentLoaded...
box.style.transition = 'opacity 1s';
box.ontransitionend = function () {
console.log('哇咔咔~~');
}
box.addEventListener('transitionend', function () {
console.log('哇咔咔~~');
});
window.addEventListener('load', function () {
//=>所有资源都加载完成触发
console.log('LOAD');
});
window.addEventListener('DOMContentLoaded', function () {
//=>只要DOM结构加载完就会触发
console.log('DOMContentLoaded');
});
//=>$(document).ready(function(){})
$(function () {
//=>JQ中的这个处理(DOM结构加载完触发)采用的就是DOMContentLoaded事件,并且依托 DOM2事件绑定来处理,所以同一个页面中,此操作可以被使用多次
});
/* JQ中的事件绑定采用的都是DOM2事件绑定,例如:on/off/one */
拖拽案例DOM2 事件绑定版
<!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>
#box,.two {
width: 50px;
height: 50px;
background: pink;
border-radius: 50%;
position: fixed;
}
.two {
background: lightgreen;
}
</style>
</head>
<body>
<div id="box"></div>
<div class="two"></div>
</body>
</html>
<script>
let box = document.querySelector('#box'),
two = document.querySelector('.two');
// mousedown mousemove mouseup
function start(e) {
// 计算最大宽度和高度
this.maxTop = document.documentElement.clientHeight - this.offsetHeight;
this.maxLeft = document.documentElement.clientWidth - this.offsetWidth;
// 记录球的初始位置和鼠标按下的位置
let pos = this.getBoundingClientRect();
// 球的初始位置
this.posLeft = pos.left;
this.posTop = pos.top;
// 鼠标按下的位置
this.startX = e.pageX;
this.startY = e.pageY;
// 为了下面的move和end正常拿到球的this,强制改变this指向,把改变this指向后的函数体绑定给window。
// this从window改成了球
// this._move是存储改变this指向之后的函数体
// 把所有需用用到的属性和方法都放在this上,
this._move = move.bind(this);
this._end = end.bind(this);
window.addEventListener('mousemove', this._move);
window.addEventListener('mouseup', this._end);
}
function move(e) {
// 改变之后的this是鼠标按下的元素
// 鼠标移动后的位置-鼠标移动前的位置 + 球的初始位置 = 球最后的位置
let top = e.pageY - this.startY + this.posTop,
left = e.pageX - this.startX + this.posLeft;
// 让top/left分别与最大值最小值比较,划定界限
top = top > this.maxTop ? this.maxTop : (top < 0 ? 0 : top);
left = left > this.maxLeft ? this.maxLeft : (left < 0 ? 0 : left);
// top left就是最后球的位置
this.style.top = top + 'px';
this.style.left = left + 'px';
}
function end() {
window.removeEventListener('mousemove', this._move);
window.removeEventListener('mouseup', this._end);
}
box.addEventListener('mousedown', start);
two.addEventListener('mousedown', start);
</script>
4、window.onload 和 $(document).ready()的区别
-
1)window.onload
- 必须等待所有资源都加载完成才会被触发执行,比ready晚
- 采用DOM0事件绑定,同一个页面只能绑定一次(一个函数),
- 想绑定多个也需要改为window.addEventListener('load', function () {})DOM2绑定方式
-
2)$(document).ready()
- 采用的是DOM2事件绑定,监听DOMContentLoaded事件实现的,是页面DOM结构渲染完成【执行】
- 同一个页面中可以使用多次(绑定不同的方法,因为基于DOM2事件池绑定机制完成的)
更多具体区别在Jquery 中有详细介绍
5、DOM0 和 DOM2 的传播的区别
-
DOM0 绑定的方法,只能在目标阶段和冒泡阶段触发执行
-
DOM2绑定的方法,我们可以控制在捕获阶段执行【事件传播机制捕获】
- 元素.addEventListener(事件类型,绑定的函数体,true/false)
七、 HTML禁止事件
oncontextmenu='return false'//禁止右键
ondragstart='return false'
onselectstart ='return false' //禁止选中
onselect='document.selection.empty()' //把选中的内容清空
oncopy='document.selection.empty()' //把选中的内容清空
onbeforecopy='return false'
onmouseup='document.selection.empty()'
转载自:https://juejin.cn/post/7210979444760625208