JavaScript事件

事件

一、事件复习

事件三要素: 事件源 + 事件名称 + 事件处理程序

  • 事件源 : 谁触发这个事件 (按钮 btn)
  • 事件名称 : 触发了什么事件 (点击click事件)
  • 事件处理程序 : 事件触发后要执行的代码(函数形式)----可以是有名字的函数,要不要加小括号?不加

1、事件类型

1.1、鼠标事件
事件描述
click左键单击
contextmenu右键单击
dblclick双击
mousedown左键按下
mouseup左键弹起
mouseover鼠标放上去(在子元素上也会触发)
mouseout鼠标离开
mouseenter鼠标放上去
mouseleave鼠标离开
mousemove鼠标移动事件
mousewheel鼠标滚轮事件

案例:星星评分

<body>
<img src="./images/rank_3.gif" alt=""><img src="./images/rank_4.gif" alt="">
<img src="./images/rank_3.gif" alt=""><img src="./images/rank_4.gif" alt="">
<img src="./images/rank_3.gif" alt=""><img src="./images/rank_4.gif" alt="">
<img src="./images/rank_3.gif" alt=""><img src="./images/rank_4.gif" alt="">
<img src="./images/rank_3.gif" alt=""><img src="./images/rank_4.gif" alt="">
</body>
<script type="text/javascript">
var imgs = document.querySelectorAll("img");
for(var i=0;i<imgs.length;i++){
    imgs[i].index = i;
    imgs[i].onmouseover=function(){
        for(var j=0;j<=this.index;j++){
            if(j%2==0){
                imgs[j].src = './images/rank_1.gif'
            }else{
                imgs[j].src = './images/rank_2.gif'
            }
        }
        for(var j=this.index+1;j<imgs.length;j++){
            if(j%2==0){
                imgs[j].src = './images/rank_3.gif'
            }else{
                imgs[j].src = './images/rank_4.gif'
            }
        }
    }
}
</script>

效果图:

1.2、浏览器事件
事件描述
load加载
scroll滚动
resize大小改变
1.3、键盘事件
事件描述
keydown按下
keyup弹起
keypress敲打

键盘事件除了用window可以触发之外,还可以使用document,或者表单元素。总之是可以选中的元素。例如div就不行。

1.4、表单事件
事件描述
submit提交表单(给form标签绑定,点击表单域中提交按钮的时候触发,在跳转之前触发)
focus获取焦点
blur失去焦点
change内容改变并失去焦点(下拉框选项/单选框状态/复选框状态改变)
inputinput的value值改变(改变了文本框的内容就会触发的事件)

使用说明:input事件在低版本的IE中不兼容,使用onpropertychange代替。

二、事件流

每个事件发生的时候,都会有一个触发并执行的过程,也就是事件的传播过程,我们称之为事件流。

简单来说,事件流就是事件从发生到执行结束的流程。

事件流包含3个阶段:捕获阶段、目标阶段、冒泡阶段

捕获阶段:改变了文本框的内容就会触发的事件

目标阶段(处理阶段):改变了文本框的内容就会触发的事件

冒泡阶段:由内向外离开目标元素的过程,此时祖宗标签的同类型事件会在这个阶段执行。
在这里插入图片描述

例:

<body>
<style>
    #big{
        width: 200px;
        height: 200px;
        border:1px solid #000;
    }
    #middle{
        width: 150px;
        height: 150px;
        background: #abcdef;
    }
    #small{
        width: 100px;
        height: 100px;
        background: red;
    }
</style>
<div id="big">
    大盒子的内容
    <div id="middle">
        中盒子的内容
        <div id="small">
            小盒子的内容
        </div>
    </div>
</div>
</body>
<script type="text/javascript">
big.onclick=function(){
    console.log(this.innerText);
    console.log("大盒子的内容完毕");
}
middle.onclick=function(){
    console.log(this.innerText);
    console.log("中盒子的内容完毕");
}
small.onclick=function(){
    console.log(this.innerText);
    console.log("小盒子的内容完毕");
}
</script>![在这里插入图片描述](https://img-blog.csdnimg.cn/09fb3e11e7e3484fa06a060dad0b3c09.png)

访问效果:
在这里插入图片描述

思考:如何让事件在捕获阶段执行?

需要使用另外一种事件绑定方式。

三、事件侦听器

1、事件绑定-同类型事件绑定多次

事件可以绑定在行内:

<button onclick="alert(123)">按钮1</button>
<button onclick="fn()">按钮2</button>
<script>
    function fn(){
        alert(456)
    }
</script>

使用on加事件类型绑定事件:

使用on来绑定事件有弊端:同一个事件只能给一个元素绑定一次。

<body>
<button id="btn">按钮</button>
</body>
<script>
btn.onclick=function(){
    console.log("第一次单击");
}
btn.onclick=function(){
    console.log("第二次单击");
}
</script>

访问效果:

解决方案:

语法:标签.addEventListener(事件类型, 函数代码, 当前事件是否在别人的捕获阶段执行,默认是false)

语法:

obj.addEventListener(type,handle,false);
# 参数1:给元素绑定的事件类型,如:click,mouseover。。。
# 参数2:处理事件的函数
# 参数3:是否在冒泡阶段执行,true在捕获阶段执行,false在冒泡阶段执行(默认false)

例:

<body>
<button id="btn">按钮</button>
</body>
<script>
document.getElementById("btn").addEventListener("click",handle,false);
function handle(){
    console.log("第一次点击");
}
document.getElementById("btn").addEventListener("click",handle1,false);
function handle1(){
    console.log("第二次点击");
}
</script>

效果:

让事件在捕获阶段执行:

<body>
<style>
    #big{
        width: 200px;
        height: 200px;
        border:1px solid #000;
    }
    #middle{
        width: 150px;
        height: 150px;
        background: #abcdef;
    }
    #small{
        width: 100px;
        height: 100px;
        background: red;
    }
</style>
<div id="big">
    大盒子的内容
    <div id="middle">
        中盒子的内容
        <div id="small">
            小盒子的内容
        </div>
    </div>
</div>
</body>
<script>
document.getElementById("small").addEventListener("click",Click,true);
document.getElementById("middle").addEventListener("click",Click,true);
document.getElementById("big").addEventListener("click",Click,true);
function Click(){
    console.log(this.innerText);
}
</script>

执行效果:

点击小盒子的时候,先触发大盒子的事件,再触发中盒子的事件,最后触发小盒子的事件。

2、事件解绑

事件一旦绑定,会一直留在内存中。大部分事件都是执行一次就不需要了,此时,占用内存的事件就造成了内存的浪费。

所以事件绑定并执行结束,不需要再次执行的时候,是需要解绑的。

因为事件绑定方式有两种,一种通过标签.on类型=函数绑定,另一种是通过事件侦听器绑定,绑定方式不同,解绑的方式也是不同的。

标签.on类型=函数绑定的事件,是在给对象的on类型属性设置值为函数,未绑定前为null,所以这种绑定方式绑定的事件,可以将标签.on类型属性赋值为null即可解绑。例:

html页面结构:

<button>按钮</button>

绑定前的标签对象属性:

js代码:

var btn = document.querySelector('button')
console.dir(btn)

图示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dEgfX0lp-1680351952272)(D:/课上练习/二阶段/预习资料/media/1666329475928.png)]

绑定后的标签对象属性:

js代码:

var btn = document.querySelector('button')
btn.onclick = function() {
    console.log('这是按钮的单击事件')
}
console.dir(btn)

图示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T076KvqX-1680351952273)(D:/课上练习/二阶段/预习资料/media/1666329524460.png)]

所以解绑就是将绑定后赋值的函数覆盖为null。

代码:

var btn = document.querySelector('button')
btn.onclick = function() {
    console.log('这是按钮的单击事件')

    // 将给btn绑定的事件解绑
    btn.onclick = null
}

这样第一次点击可以输出,然后就将btn的事件解绑了,下次再点击就无效了。

通过事件侦听器绑定的事件,系统提供了对应的解绑函数:removeEventListener(事件类型, 绑定的函数)

例:

btn.addEventListener('click', fn)
function fn() {
	console.log('这是btn的单击事件')
    // 将给btn绑定的事件解绑
    btn.removeEventListener('click', fn)
}

这样第一次点击可以输出,然后就将btn的事件解绑了,下次再点击就无效了。

注意:解绑的必须和绑定是同一个函数 - 如果绑定的是匿名函数,这个事件是无法解绑的。

因为同样的匿名函数,每写一次,都是在定义一个新的匿名函数,两个匿名函数会在堆中创建两个空间存储。

思考:如何让事件不冒泡?

需要使用事件对象。

四、事件对象

浏览器为事件提供了一个对象,用来记录事件的各种具体信息,例如,鼠标点击的位置、鼠标按键的信息、键盘的键码。

这就是事件对象。

<body>
<button id="btn">按钮</button>
</body>
<script type="text/javascript">
btn.onclick=function(){
    console.log(window.event);
}
</script>

点击访问结果:

从结果中可以看出,这个事件对象中包含了事件的类型、鼠标点击的位置,在屏幕中和在浏览器中的位置。点开的话,里面还有很多信息。

还有另外一种写法:

btn.onclick=function(e){
    console.log(e);
}

以前的时候,前面的写法是针对IE浏览器的,下面的写法是针对W3C标准浏览器的。上面的写法,现在基本都兼容了;下面这种写法,在IE低版本浏览器中还不兼容。

兼容所有浏览器的写法:

btn.onclick = function(e){
    var ev = e || window.event
    console.log(ev);
}

如果是行内绑定的事件,就将事件对象当做参数传进来即可。且必须是event。

例:

<button onclcik="fn(event)">
    按钮
</button>
<script>
    function fn(e){
        console.log(e);
    }
</script>

事件绑定后,事件函数是由浏览器监听到用户触发事件后,去调用这个函数执行的

浏览器在调用这个函数的时候,会给函数传递一个实参

实参是一个对象 - 事件对象:其中记录了很多跟当前事件相关的信息

1、获取事件类型

e.type // 事件的类型

例:

<button id="btn">
    按钮
</button>
<script>
    btn.onclick=function(e){
        var e = e || window.event;
        console.log(e.type); // MouseClick
    }
    btn.onmouseover=function(e){
        var e = e || window.event;
        console.log(e.type); // MouseOver
    }
</script>

2、获取鼠标按键码

e.button // 鼠标按键的信息

0表示左键,1表示鼠标滚轮,2表示右键,例:

<body>
<button id="btn">按钮</button>
</body>
<script type="text/javascript">
btn.onmousedown = function(e){
    var ev = e || window.event;
    var code = ev.button;
    if(code == 0){
       console.log("您点击的是左键");
    }else if(code == 1){
        console.log("您点击的滚轮");
    }else if(code == 2){
        console.log("您点击的是右键");
    }else{
        console.log("不知道你点击的是什么");
    }
}
</script>

3、获取键盘按键码

e.keyCode // 键盘的键码

常见的按键码:

  • 13: 回车键(enter)
  • 32: 空格键(space)
  • 数字和字母的键码是数字或字母(对应大写字母)对应的阿斯克码

例:

<body>
<button id="btn">按钮</button>
</body>
<script type="text/javascript">
document.onkeypress=function(e){
    var ev = e || window.event;
    console.log(ev.keyCode);
}
</script>

组合按键的判断:

  • altKey :alt 键和别的键一起按下得到 true,否则得到 false
  • shiftKey :shift 键和别的键一起按下得到 true,否则得到 false
  • ctrlKey :ctrl 键和别的键一起按下得到 true,否则得到 false
  • metaKey:win键和别的键一起按下返回true,否则返回false
document.onkeyup = function (e) {
  e = e || window.event
  keyCode = e.keyCode
  
  if (e.shiftKey && keyCode === 97) {
    console.log('你同时按下了 shift 和 a')
  }
}

4、阻止事件冒泡

在事件对象中,有一个方法用来阻止事件冒泡,这个方法叫做e.stopPropagation

三元运算: 
事件对象.stopPropagation ? 事件对象.stopPropagation() :事件对象.cancelBubble = true`

例:

<body>
<style>
    #big{
        width: 200px;
        height: 200px;
        border:1px solid #000;
    }
    #middle{
        width: 150px;
        height: 150px;
        background: #abcdef;
    }
    #small{
        width: 100px;
        height: 100px;
        background: red;
    }
</style>
<div id="big">
    大盒子的内容
    <div id="middle">
        中盒子的内容
        <div id="small">
            小盒子的内容
        </div>
    </div>
</div>
</body>
<script type="text/javascript">
big.onclick=function(){
    console.log(this.innerText);
    console.log("大盒子的内容完毕");
}
middle.onclick=function(){
    console.log(this.innerText);
    console.log("中盒子的内容完毕");
}
small.onclick=function(e){
    var e = e || window.event;
    e.stopPropagation();
    console.log(this.innerText);
    console.log("小盒子的内容完毕");
}
</script>

此时,点击小盒子,不再触发父元素的事件。

阻止事件冒泡在IE浏览器中有兼容问题,在低版本IE浏览器中,需要使用另外一种写法:

e.cancelBubble=true; # IE低版本浏览器

例:

small.onclick=function(e){
    var e = e || window.event;
    e.cancelBubble=true;
    console.log(this.innerText);
    console.log("小盒子的内容完毕");
}

为了兼容IE低版本浏览器,使用兼容写法:

small.onclick=function(e){
    console.log(this.innerText);
    console.log("小盒子的内容完毕");
    var e = e || window.event;
    if(e.stopPropagation){
   		e.stopPropagation
    }else{
        e.cancelBubble=true;
    }
}

5、阻止默认行为

具有默认行为的常见的两个标签

链接<a href="/index.php">点我</a>  往属性href指定的地址跳转
提交按钮<input type=”submit”>   往form表单属性action指定的地址跳转

阻止默认行为的方法:

e.preventDefault -- IE浏览器不兼容


三元运算:事件对象.stopPropagation ? 事件对象.stopPropagation() :事件对象.cancelBubble = true`
  1. 给链接地址设置为javascript:;javascript:void(0)

  2. 在事件中最后return false

  3. 通过对象阻止,代码如下:

    function stopDefault(e) {
        var e = e || window.event; 
        if (e.preventDefault){
            e.preventDefault();   // w3c标准浏览器
        }else{
            e.returnValue = false; // IE浏览器
        }
    }
    

6、获取鼠标坐标点

鼠标相对当前标签的位置:事件对象.offsetXoffsetY

<body>
<style>
#box{
    width: 200px;
    height: 200px;
    border: 1px solid #000;
}
</style>
<div id="box"></div>
</body>
<script type="text/javascript">
box.onclick = function(e){
    var ev = e || window.event;
    console.log(ev.offsetX,ev.offsetY);
}
</script>

鼠标相对浏览器的位置使用:事件对象.clientXclientY

<body>
<style>
    #box{
        width: 200px;
        height: 200px;
        border: 1px solid #000;
        margin: 100px;
    }
</style>
<div id="box"></div>
</body>
<script type="text/javascript">
box.onclick = function(e){
    var ev = e || window.event;
    console.log(ev.clientX,ev.clientY);
}
</script>

使用说明:不管页面滚动到哪里,都是根据窗口来计算坐标。(不会随着滚动条的滚动而发生改变)

鼠标相对整个网页的位置:会随着滚动条的滚动而加大,使用:pageXpageY

<body>
<style>
    #box{
        width: 20px;
        border: 1px solid #000;
        margin: 100px;
    }
</style>
<div id="box">
        没有什么能够阻挡
        你对自由的向往
        天马行空的生涯
        你的心了无牵挂
        穿过幽暗的岁月
        也曾感到彷徨
        当你低头的瞬间
        才发觉脚下的路
        心中那自由的世界
        如此的清澈高远
        盛开着永不凋零
        蓝莲花
</div>
</body>
<script type="text/javascript">
box.onclick = function(e){
    var ev = e || window.event;
    console.log(ev.pageY);
}
</script>

横向坐标也是一样的。

7、事件委托

事件委托是指子标签委托父标签处理事件。

传统的给每个元素绑定事件:

<ul>
	<li>首页</li>
	<li>公司介绍</li>
	<li>产品中心</li>
</ul>
<script>
var oLis = document.getElementsByTagName("li"); // 获取到所有li
for(var i in oLis){
	oLis[i].onclick = function(){
		alert(this.innerText);
	}
}
</script>

代码的缺点:

  1. li标签比较多的时候,性能特别差,毕竟使用for循环相当于绑定了多次

  2. 当动态给li添加元素的时候,新元素没有事件绑定

    ul>
    	<li>首页</li>
    	<li>公司介绍</li>
    	<li>产品中心</li>
    </ul>
    <button id="btn">添加新元素</button>
    <script>
    btn.onclick = function(){
    	var oNewLi = document.createElement("li");
    	oNewLi.innerText = "新闻中心";
    	var oUl = document.getElementsByTagName("ul")[0];
    	oUl.appendChild(oNewLi)	
    }
    var oLis = document.getElementsByTagName("li"); // 获取到所有li
    for(var i in oLis){
    	oLis[i].onclick = function(){
    		alert(this.innerText);
    	}
    }
    </script>
    

    分析:当点击按钮给ul添加新元素以后,新元素不具备点击事件,点击没有效果

解决方案:使用事件委托,将所有子元素的点击事件委托给父元素

<ul>
	<li>首页</li>
	<li>公司介绍</li>
	<li>产品中心</li>
</ul>
<button id="btn">添加新元素</button>
<script>
var oUl = document.getElementsByTagName("ul")[0];
btn.onclick = function(){
	var oNewLi = document.createElement("li");
	oNewLi.innerText = "新闻中心";
	oUl.appendChild(oNewLi)	
}
oUl.onclick = function(e){
	var ev = e || window.event;
	// 获取到单击的目标元素dom对象
	var target = ev.target || ev.srcElement;
	// 判断是否是li标签
	if(target.nodeName == "li"){
		// 完成业务逻辑
		alert(target.innerText);
	}
}
</script>

原理:利用事件冒泡。触发子标签事件的时候,会在冒泡阶段触发父标签的事件。

使用事件委托的好处:

  1. 提高事件的绑定效率
  2. 后续动态添加的节点也有事件的处理

关键技术:获取精准的目标元素(事件对象.target),对其进行判断

nclick = function(){
alert(this.innerText);
}
}


分析:当点击按钮给ul添加新元素以后,新元素不具备点击事件,点击没有效果

解决方案:使用事件委托,将所有子元素的点击事件委托给父元素

```html
<ul>
 <li>首页</li>
 <li>公司介绍</li>
 <li>产品中心</li>
</ul>
<button id="btn">添加新元素</button>
<script>
var oUl = document.getElementsByTagName("ul")[0];
btn.onclick = function(){
 var oNewLi = document.createElement("li");
 oNewLi.innerText = "新闻中心";
 oUl.appendChild(oNewLi)	
}
oUl.onclick = function(e){
 var ev = e || window.event;
 // 获取到单击的目标元素dom对象
 var target = ev.target || ev.srcElement;
 // 判断是否是li标签
 if(target.nodeName == "li"){
 	// 完成业务逻辑
 	alert(target.innerText);
 }
}
</script>

原理:利用事件冒泡。触发子标签事件的时候,会在冒泡阶段触发父标签的事件。

使用事件委托的好处:

  1. 提高事件的绑定效率
  2. 后续动态添加的节点也有事件的处理

关键技术:获取精准的目标元素(事件对象.target),对其进行判断

注意:事件委托底层就是通过事件冒泡来完成的,先触发父元素的事件,在通过事件对象的target属性找到具体的目标元

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值