DOM事件
在面向对象当中,其实有三个点存在
1、属性:用于描述对象
2、方法:对当前对象使用这个方法操作它
3、事件:用于交互,事件是会产品影响(结果)
DOM中的事件是指DOM的交互行为,可以于DOM产生交互的向主要两个
1、DOM与做用域进行事件交互
2、DOM与系统进行事件交互
事件是DOM对象里面的一个特殊属性,它需要经过一定的条件触发,普通属性后面跟的都是属性值,而事件作为一个特殊属性它接收属性值是一个function,或者直接写一个表达式也可以
<input type="button" onclick="console.log('hello world')" value="点我">
<button onclick="sayHello()">再来点我</button>
<script type="text/javascript">
function sayHello(){
console.log("hello world");
}
</script>
用户触发事件,事件调用方法
按照w3c的标准,DOM事件被划分成两个级别
0级DOM事件
这是一个基本DOM事件,0级事件本质上来讲就是DOM对象的一个属性,只是这个属性比较特殊,这些属性都是有一些共同的特点
1、这些属性都是on开头
2、这些属性的属性值一般都是一个function
在0级事件里面,我们的事件又可以大致分为以下几类
1、鼠标事件
2、键盘事件
3、文档事件
4、其他事件
现在我们就来学习这些事件
在学习之前,我们要先要学会事件的使用,也就是事件的绑定
事件的绑定
第一种绑定方式
<input type="button" onclick="console.log(this)" value="点我">
当我们用鼠标去点击上面的按钮时,就会触发 onclick
事件,这是事件就会调用后面的方法,运行console.log打印操作
在上面的代码里面,我们的this指向的是这个按钮,这就是说明一个情况,console.log这个方法在调用的时候是DOM对象在调用,而不是用户在调用,用户仅仅是触发了onclick点击事件而已
第二种绑定方式
<button onclick="sayHello()">再来点我</button>
<script type="text/javascript">
function sayHello(){
console.log("hello world");
}
</script>
上面的sayHello方法是我们自定义的方法,然后通过将方法赋值给触发事件的方式来绑定事件
第三种绑定方式
<button id="btn1">第三种绑定方式</button>
<script>
var btn = document.querySelector("#btn1");
function abc(){
console.log("haha");
}
//这里注意:赋值的时候赋的是方法体,而不是方法结果,因为现在方法的调用不再是它自己,而是事件
btn.onclick = abc;
</script>
上面的赋值过程,我们现在还可以用面向对象的基础来完成,也就是匿名函数
btn.onclick = function(){
console.log(this);
console.log("haha");
}
上面的三种方法,第三种情况适合对于元素的批量绑定
<ul class="ul1">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
<script type="text/javascript">
//请给以上li绑定一个事件
var liList = document.querySelectorAll(".ul1>li");
for(var i = 0;i < liList.length;i++){
liList[i].onclick = function(){
this.classList.toggle("active");
}
}
</script>
0级DOM事件列表
1、onclick 鼠标点击事件
2、ondblclick 鼠标双击事件
3、onmousemove 鼠标移动事件
4、onmousedown 鼠标按下事件
5、onmouseup 鼠标松开事件
补充,鼠标的点击事件其实就是鼠标按下和鼠标松开的集合体
6、onmouseenter 鼠标进入事件
7、onmouseleave 鼠标离开事件
补充一点:以上两个事件是很老的事件,这个两个事件是不支持后期的【事件冒泡】
8、onmouseover 鼠标进入事件
9、onmouseout 鼠标离开事件
补充:这两个事件可以形成一组事件,它是上面6,7的替代,这两个事件支持【事件冒泡】
10、onmousewheel 鼠标中间滚动事件
11、onkeydown 键盘按下事件
12、onkeyup 键盘松开事件
13、onkeypress 键盘敲击事件
补充:onkeypress于onkeydown很类似,但是也有区别,onkeydown可以捕捉任意按键,但是onkeyress不能捕捉功能键,比如 ctrl、alt、Fn、home、end、F1-F12都不行
14、onload 加载事件
15、onerror 加载错误事件
16、oninput 输入框输入的时候触发的事件
17、onfocus 表单元素获取光标的时候触发的事件(获取焦点)
18、onblur 表单元素失去焦点
19、onchange 表单里面的值改变的时候触发的事件
20、onsubmit 表单被提交的时候触发的事件
21、onreset 表单被重置时触发的事件
22、oncontextmenu 右键菜单事件
23、onscroll 滚动条滚动的时候触发的事件
0级事件批量绑定
场景: 现在页面上有10个按钮,我们希望对这10个按钮都绑定一个onclick事件,每个按钮点击的时候,在控制台打印当前按钮的序号,怎么实现?
<button class="btn1" type="button">0</button>
<button class="btn1" type="button">1</button>
<button class="btn1" type="button">2</button>
<button class="btn1" type="button">3</button>
<button class="btn1" type="button">4</button>
<button class="btn1" type="button">5</button>
<button class="btn1" type="button">6</button>
<button class="btn1" type="button">7</button>
<button class="btn1" type="button">8</button>
<button class="btn1" type="button">9</button>
将上面的按钮批量绑定
var btns = document.querySelectorAll(".btn1");
for(var i = 0;i < btns.length;i++){
btns[i].onclick = function(){
console.log(btns[i].innerText);
}
}
注意:上面的写法是错误的, 因为for循环已经执行完毕,i的值已经固定成10了,这个时候当我们在去触发事件,事件再调用方法的时候,方法里面的要调用的i的值就是10,所以实际执行的是 btns[10] , 但是集合里面没有索引值为10个这个元素,所以就错了
要解决以上的问题,以下两种方式
第一种:通过this关键字解决
var btns = document.querySelectorAll(".btn1");
for(var i = 0;i < btns.length;i++){
btns[i].onclick = function(){
console.log(this.innerText);
}
}
这里注意,是用户触发了事件,事件再调用方法,所以事件方法里面的this指向的是当前DOM对象
第二种:通过闭包函数的立即执行
var btns = document.querySelectorAll(".btn1");
for(var i = 0;i < btns.length;i++){
btns[i].onclick = (function(j){
return function(){
console.log(btns[j].innerText);
}
})(i);
}
0级事件中的this指向
我们学习过三种0级事件的绑定方式
1、直接在事件的后面写js代码
2、在事件的后面写调用方法名
3、直接把方法赋值给DOM的事件上面
第一种方式
<button type="button" onclick="console.log(this)"></button>
这个时候this指向的是当前的DOM对象(button)
第二种方式
<button type="button" onclick="abc()"></button>
<script>
function abc(){
console.log(this);
}
</script>
注意:这种情况下this指向的是window对象
问题:我们能不能在这种情况下改变this 的指向,让它指向当前的DOM对象
我们可以通过call/apply的调用,来改变this的指向
<button type="button" onclick="abc.call(this)"></button>
<script>
function abc(){
console.log(this);
}
</script>
第三种方式
<button type="button" id="btn1"></button>
<script>
var btn1 = document.querySelector("#btn1");
btn1.onclick = function(){
console.log(this);
}
</script>
事件对象event
事件对象是所有事件都具备的,当用户触发事件的时候,事件会调用方法,事件在调用方法的过程当中会向当前的方法内部注入一个参数,这个参数就是事件对象参数event
获取事件对象
获取事件对象参数三种方式
第一种方式
<button type="button" onclick="console.log(event)">按钮</button>
第二种方式
<button type="button" onclick="abc(event)"></button>
<script>
function abc(event){
console.log(event);
}
</script>
第三种方式
<button type="button" id="btn1"></button>
<script>
var btn1 = document.querySelector("#btn1");
btn1.onclick = function(e){
e = event || window.event; //兼容性写法,保证我们在不同的浏览器里面都可以正确的获取到事件对象
}
</script>
上面获取事件对象方式中要注意一下,浏览器不同会造成我们获取event对象的时候代码不太一样,谷歌核心的浏览器是会像事件方法中注入event参数,而火狐和IE不会注入这个参数,它会直接附加在window对象上面,所以我们需要
event = event || window.event;
这种方式来获取事件对象
分析事件对象
mouseEvent事件
- 1、altkey : 事件触发时是否按下了alt键
- 2、ctrlkey:事件触发时是否按下了ctrl键
- 3、shiftkey:事件触发时是否按下了shift键
- 4、button:代表当前鼠标事件是由哪个键触发的,0代表鼠标左键,1代表鼠标中键,2代表鼠标右键
- 5、clientX / clientY :鼠标距离浏览器左上的横纵坐标
- 6、screenX / screenY :鼠标距离屏幕左上角的横纵坐标
- 7、X / Y:这两个属性于clientX / clientY保持一致,只是浏览器兼容性的问题,如果想要获取坐标
var x = event.X || event.clientX
- 8、type:当前事件类型
keyboardEvent事件
1、keyCode:触发事件时的按键编码,它不区别大小写,也就是A和a按键编码是一样的,都是65
2、key:触发事件时的按钮内容
3、repeat:当前的键盘事件是否时上一次事件没有松开以后自动触发的
0级事件的特点
0级事件是DOM中最基本的事件
1、它是以on开头
2、它的属性值一般是一个function
3、0级事件不能多次赋值,多次赋值,后面的会把前面的盖掉
4、0级事件会进行事件传播,但是传播方向不可控【事件的传播分别两种,分别是事件冒泡和事件捕获】
场景一:事件的多次赋值
<button type="button" onclick="console.log('这是第一个方法')" id="btn">按钮</button>
<script type="text/javascript">
var btn = document.querySelector("#btn");
function abc(){
console.log("这是方法abc");
}
btn.onclick = abc;
btn.onclick = function(){
console.log("这又是一个方法")
}
</script>
这个时候我们看到上面的onclick赋值了多次,但是只会以最后一次为主,0级事件如果想清除,需要给当前事件赋值一个null
场景二:事件的传播行为
一个DOM对象上面的事件会传播给另外一个DOM对象
<div class="div1">
<button type="button" id="btn1">按钮</button>
</div>
<script type="text/javascript">
var div1 = document.querySelector(".div1");
var btn1 = document.querySelector("#btn1");
div1.onclick = function(){
console.log("我是div1");
}
btn1.onclick = function(){
console.log("我是btn1");
}
</script>
上面的代码中,我们点击div1中的btn1按钮的时候,我们发现两个事件都被触发了,这就说明了一点,内部按钮btn1的onclick事件行为传递给了外面元素div1的onclick上面去了,这种现象我们就叫事件冒泡
事件冒泡
它指的是由内向外进行传播
取消事件冒泡
event.cancelBubble = true; //取消事件冒泡
event.stopPropagation(); //停止事件的传播
事件的触发者与绑定者
div1.onclick = function(event){
event = event || window.event;
console.log("target",event.target);
console.log("currentTarget",event.currentTarget);
}
上面的代码我们点击了btn1这个按钮,这个按钮本身是没有绑定事件的,但是它会冒泡到外面div1上面去,外部的div1上面是有绑定事件的,所以btn1的点击行为传播到了父级的div1上面,因此触发绑定在div1上面的事件,事件就会调用绑定在div1上面的事件方法
这里,我们理清楚,也就是说btn1是触发,div1绑定
target
指事件的触发者
currentTarget
指的是事件的绑定者
事件冒泡实现事件委托
我们先来看一下代码:
<ul class="ul1">
<li>1</li>
<li class="active">2</li>
<li>3</li>
<li class="active">4</li>
</ul>
<button type="button" onclick="addNewLi()">新添加一个li</button>
<script type="text/javascript">
//bindLiEvent给ul1中的li批量绑定onclick事件,并赋值事件方法
function bindLiEvent(){
//获取到ul1中所有的li
var lis = document.querySelectorAll(".ul1>li");
//遍历所有的li,进行事件的绑定
for(var i = 0;i < lis.length;i++){
//每一次循环给一个li绑定一个单击事件
lis[i].onclick = function(){
//单击事件赋值一个在控制台打印自己元素内的文本内容的事件方法
console.log(this.innerText);
}
}
}
//在这里先执行一遍,给默认存在的4个li绑定事件
bindLiEvent();
//addNewLi给ul1里面的后面添加新的li
function addNewLi(){
//创建一个li元素
var newLi = document.createElement("li");
//给新创建的li元素添加文本内容
newLi.innerText = "新增的项";
//获取ul1,然后给ul1里面的后面添加新建的带文本内容的li
document.querySelector(".ul1").appendChild(newLi);
//执行bindLiEvent
bindLiEvent();
}
</script>
在上面的代码里面,我们每次添加一个新的li之后,都需要重新执行一遍事件绑定,这样很消耗性能
我们尝试进行一下性能优化
我转换思考一下,能否把这个事件绑定在li的父级ul1上面,然后让里面的元素都通过事件冒泡来执行外边绑定的事件
//我们把事件绑定在ul1这个元素上面
//ul1的内部元素的所有事件最后都会冒泡传递到ul1上面
var ul1 = document.querySelector(".ul1");
ul1.onclick = function(event){
event = event || window.event;
//target指向是你当前点击的这个li
console.log(event.target.innerText);
}
function addNewLi(){
//创建一个li元素
var newLi = document.createElement("li");
//给新创建的li元素添加文本内容
newLi.innerText = "新增的项";
//获取ul1,然后给ul1里面的后面添加新建的带文本内容的li
document.querySelector(".ul1").appendChild(newLi);
}
上面的代码就是最基本的事件委托代码,onclick事件本来应该是绑定在li上面的,但是现在委托给它的父级元素ul进行绑定,然后利用事件冒泡原理进行传递,这种现象我们就叫事件委托
现在我们想让指定的元素成为触发者,只有点击指定的元素才会触发事件,那么这里怎么判断触发者是指定的元素,这里可以使用DOM里面的一个API方法 matches()
来完成判断
var ul1 = document.querySelector(".ul1");
ul1.onclick = function(event){
event = event || window.event;
if(event.target.matches("li.active")){
//target指向是你当前点击的这个li
console.log(event.target.innerText);
}
}
事件委托中的this
根据对象调用方法的this指向原则,方法里面的this指向当前调用这个方法的对象,而这个方法又是绑定在事件绑定者身上,所以事件委托中的this指向的是事件的绑定者
事件先于响应
有些html标签在经过JavaScript的处理之后,还是会做除一些响应,比如a标签
<a href="https://www.baidu.com" id="a1">百度一下</a>
<script type="text/javascript">
var a1 = document.querySelector("#a1");
a1.onclick = function(){
alert("hello world");
}
</script>
在上面的代码中,当我们点击a标签的时候,它会执行onclick事件,然后再去执行a标签的响应操作跳转页面
我们可以再事件当中取消这个响应操作。只需要再事件方法中返回一个false就可以了
a1.onclick = function(){
alert("hello world");
return false;
}
同样的,我们也可以通过return false来取消表单的重置按钮的重置功能
<form action="">
<input type="text">
<input type="text">
<button type="reset" id="btn">重置</button>
</form>
<script type="text/javascript">
var btnReset = document.querySelector("#btn");
btnReset.onclick = function(){
return false;
}
</script>