事件委托
事件委托的用法,给目标元素的父级绑定事件,然后通过父级元素去找目标元素
事件委托的概念
- 把我们需要做的事情交给别人去做
- 因为我们的冒泡机制,点击子元素,父级元素也会触发相同的事件
- 所以我们可以通过子元素的事件委托给父级来做
事件委托的优缺点
- 优点1:减少事件的绑定,带来的内存消耗,想能降低问题
- 优点2:可以随意给动态的子元素,绑定事件
- 缺点:代码比较繁琐,需要寻找目标target
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var oUl = docuemnt.querySelector('ul')
oUl.addEventListener('click', function (evt) {
var e = evt || window.event
// 判断你点击的是 li
if (e.target.nodeName != 'LI') {
return;
}
// 确定点击的是 li
// 因为当你点击在 ul 上面的时候,nodeName 应该是 'UL'
// 去做点击 li 的时候该做的事情了
console.log('我是 li,我被点击了')
})
</script>
</body>
事件监听
事件监听器, 是JS事件中的一种事件触发机制, 我们可以通过添加事件监听器的方式给元素添加事件及执行函数
- 通过
on来帮绑定事件,无法给同一个dom事件,绑定多个监听事件,会被后写的给覆盖 - 通过添加事件监听器的方式
addEventListener(),可以给同一个dom事件绑定多个监听事件 removeEventListener可以移除事件监听
添加事件监听
- box.addEventListener(“click”, func, false) : 给box元素添加点击事件(click), 以及事件执行函数func. 针对该函数的三个参数作说明:
- 第一个参数(“click”) : 事件名称(前面没有on)
- 第二个参数(func): 事件执行函数名称(没有双引号, 也没有())
- 第三个参数(false/true) : true:捕捉(反向冒泡),默认false,为冒泡
- 第三个参数为对象:
- {once:true,capture:true}
- once:可写可不写,写了且值为true,则只能点击一次
- capture:捕获一定要写true
<style>
.box{
width: 300px;
height: 300px;
background-color: red;
}
</style>
<body>
<div class="box"></div>
<button>绑定事件</button><button>移除事件</button>
<script>
var oBox = document.querySelector(".box");
var oBtns = document.querySelectorAll("button");
oBox.onclick = function(){
console.log("11111");
}
function fn1(){
console.log("22222");
}
function fn2(){
console.log("33333");
}
oBtns[0].onclick = function(){
oBox.addEventListener("click",fn1);
oBox.addEventListener("click",fn2)
}
//移除事件监听
oBtns[1].onclick =function(){
oBox.removeEventListener("click",fn1);
}
</script>
</body>
注意问题
- 第二个参数是一个回调函数(局部),而不是通过全局函数,在第二个参数调用函数名
- 按钮重复点击,绑定事件就会越多
- 核心问题,每点击一次,就会创建一个新的函数,创建新的函数,地址不同
- 如何避免?要保证添加的函数的唯一性
- 函数是引用类型,添加和移除必须是同一个地址,移除的不是同一个地址,移除会失效
移除事件监听
- box.removeEventListener(“click”, func) : 将box元素的点击事件(click), 以及对应的事件执行函数func移除
注: 这里只会删除使用box.addEventListener()方法添加的事件监听器移除监听事件必须跟添加事件监听的第三个参数保持一致,否则会失效
//移除事件监听
oBtns[1].onclick =function(){
oBox.removeEventListener("click",fn1);
}
事件流
事件流是描述的从页面接受事件的顺序,当几个都具有事件的元素层叠在一起的时候, 那么你点击其中一个元素,并不是只有当前被点击的元素会触发事件,而层叠在你点击范围的所有元素都会触发事件。
事件流,当元素叠在一起,点击某个元素,事件会传递- 事件流包括两种模式:冒泡和捕获
- 事件流包括三个阶段:1.冒泡-->2.目标-->3.捕获
- 事件冒泡: 从内向外传递事件
- 事件捕获: 从外向内传递事件
事件冒泡
- 事件冒泡: 从内向外传递事件
<style>
div{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
.big{
width: 300px;
height: 300px;
background-color: red;
.center{
width: 200px;
height:200px;
background-color: yellow;
.small{
width: 100px;
height: 100px;
background-color: greenyellow;
}
}
}
</style>
<body>
<div class="big">big
<div class="center">center
<div class="small">
small
</div>
</div>
</div>
<script>
var oBig = document.body.querySelector(".big");
var oCenter = oBig.querySelector(".center");
var oSmall = oBig.querySelector(".small");
oBig.onclick = function(){
console.log("oBig");
}
oCenter.onclick = function(){
console.log("oCenter");
}
oSmall.onclick = function(){
console.log("oSmall");
}
</script>
</body>
- 点击small盒子,会从里向外依次触发点击事件
阻止事件冒泡
- 阻止事件冒泡都只会阻止自己不往外面冒泡出去,在指定不想再继续传递事件的节点的事件执行函数中使用
- 方法1:取消冒泡 IE使用
e.cancelBubble = true; //ie下写法 了解
- 方法2:停止传播 非IE使用
e.stopPropagation();//重点
- 兼容写法:
if(e.stopPropagation){
e.stopPropagation();
}else{
e.cancelBubble = true
};
事件捕获
- 事件捕获:从外向内传递事件
- addEventListener 默认是冒泡(第三个参数不写或是false),捕获就需要把第3个参数,设置
true或是把第三个参数,设置一个对象 - addEventListener 和 removeEventListener的
第三个参数必须相同,同为true或false否则移除不会生效
<style>
div{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
.big{
width: 500px;
height: 500px;
background-color: red;
.center{
width: 400px;
height: 400px;
background-color: yellow;
.small{
width: 300px;
height: 300px;
background-color: greenyellow;
}
}
}
</style>
<body>
<div class="big">big
<div class="center">center
<div class="small">
small
</div>
</div>
</div>
<script>
var oBig = document.body.querySelector(".big");
var oCenter = oBig.querySelector(".center");
var oSmall = oBig.querySelector(".small");
oBig.addEventListener("click",function(){
console.log("oBig");
},{
// once:true, //只能点击一次,可写可不写
capture:true, //捕获一定要写capture:true
})
oCenter.addEventListener("click",function(){
console.log("oBoCenterox");
},{
// once:true,
capture:true,
})
oSmall.addEventListener("click",function(){
console.log("oSmall");
},{
//once:true,
capture:true,
})
</script>
</body>
默认行为
-
默认行为,就是不用我们注册,它自己就存在的事情
- 比如我们点击鼠标右键的时候,会自动弹出一个菜单
- 比如我们点击 a 标签的时候,我们不需要注册点击事件,他自己就会跳转页面
- 表单的提交,重置行为
- 图片的拖拽行为
- …
-
这些不需要我们注册就能实现的事情,我们叫做 默认事件
阻止默认行为
- 方法1: return false
- 方法2: e.returnValue = false
- 方法3: e.preventDefault()
- 方法4: 兼容写法
<body>
<a href="http://baidu.com">百度</a>
<script>
var oA = document.querySelector("body a");
oA.onclick = function(evt){
//1.重点1
// return false; //语法
var e = evt || window.event;
//2.(了解)
// e.returnValue = false;
//3.重点2
// e.preventDefault();
//4.兼容写法(了解)
if(e.preventDefault){
e.preventDefault()
}else{
e.returnValue = false
}
}
</script>
</body>
阻止右键菜单
- 在之前使用event对象的button属性时, 点击鼠标右键会弹出系统菜单, 如果我们想要创建自己的右键菜单, 则需要先阻止默认的右键菜单
document.oncontextmenu = function(){
console.log("右键被按下");
return false;
}
拖拽
- 按住元素后移动位置, 最后松开的过程
- 主要通过三个事件控制
- onmousedown: 鼠标按下
- onmousemove: 鼠标移动
- onmouseup: 鼠标松开
<style>
.box{
width: 100px;
height: 100px;
background-color: skyblue;
position: absolute;//盒子动不动,就看这个
}
</style>
<body>
<div class="box"></div>
<script>
var oBox = document.querySelector(".box");
oBox.onmousedown = function(e){
//获取鼠标点击位置到元素左上角的距离
//可以使用e.offsetX和e.offsetY获取,但是在页面结构复杂情况下,不建议使用
var disX = e.pageX-this.offsetLeft;
var disY = e.pageY -this.offsetTop;
//鼠标按住并移动,重新设置盒子的left和top
//获取鼠标点击到页面左上角距离 - 鼠标点在元素自身的距离
document.onmousemove = function(e){
oBox.style.left = e.pageX- disX+"px";
oBox.style.top = e.pageY -disY+"px";
}
//鼠标松开时,鼠标移动失效
document.onmouseup = function(){
document.onmouseup = document.onmousemove=null;
}
}
</script>
</body>
三大'DOM'的属性
offsetParent
- 找带有就近定位的父元素,如果父级们都没有定位,默认找body 返回dom对象
offsetLeft,offsetTop
- 获取当前元素到带有定位父级元素的距离,默认到body的距离 返回数值
offsetWidth,offsetHeight
- 获取自身元素的宽与高 = 自身宽高+padding+border,就是不包含margin 返回数值
封装一个通用的方法,获取某个元素始终到body的距离
//obj是一个dom对象
function offset(obj){
var o={
left:obj.offsetLeft,
top:obj.offsetTop
};
while(obj.offsetParent){
//如果obj.offsetParent是null,就说明没有定位的父元素了,null就是false,false中断循环
obj = obj.offsetParent;
o.left = obj.offsetLeft + o.left;
o.top +=obj.offsetTop;
}
return o;
}