事件
介绍:事件是在编程时系统内发生的动作或者发生的事情,系统会在事件出现时产生或触发某种信号,并且会提供一个自动加载某种动作(列如:运行一段代码)的机制。例如:如果用户在网页上单击一个购买按钮,会显示一个购买确认信息框来响应这个动作。
具体的说事件是可以被JavaScript侦测到的行为,网页中每一个元素都可以产生某些可以出发JavaScript函数的事件,在网页中, 事件在浏览器窗口中被触发并且通常被绑定到窗口内部的特定部分 — 可能是一个元素、一系列元素、被加载到这个窗口的 HTML 代码或者是整个浏览器窗口。举几个可能发生的不同事件:
- 用户在某个元素上点击鼠标或悬停光标。
- 用户在键盘中按下某个按键。
- 用户调整浏览器的大小或者关闭浏览器窗口。
- 一个网页停止加载。
- 提交表单。
- 发生错误。
概念:每个可用的事件都会有一个事件处理器,也就是事件触发时会运行的代码块。当我们定义了一个用来回应事件被激发的代码块的时候,我们说我们注册了一个事件处理器。注意事件处理器有时候被叫做事件监听器——从我们的用意来看这两个名字是相同的,监听器留意事件是否发生,然后处理器就是对事件发生做出的回应。
例子:
<button>Change color</button>
<script>
// 使用Document.querySelector() 函数获取 button 并用 btn 变量存储。
const btn = document.querySelector('button');
// 定义了一个返回随机数字的函数。
function random(number) {
return Math.floor(Math.random()*(number+1));
}
// 这里就是事件处理器。在 button 元素上可触发一系列的事件,因此也就可以使用事件处理器监听“点击”这个事件。
// 并将一个匿名函数(这个赋值函数包括生成随机色并赋值给背景色的代码)赋值给“点击”事件处理器参数,
// 只要点击事件在<button>元素上触发,该段代码就会被执行。即每当用户点击它时,都会运行此段代码。
btn.onclick = function() {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}
</script>
事件的绑定方式
介绍:JavaScript可以通过多种不同的方法将事件侦听器代码添加到网页,以便在关联的事件被触发时运行它。这种方法我们称之为事件的绑定方式
一、事件处理器属性
介绍:上面的例子中 onclick
是被用在这个情景下的事件处理器的属性,它就像 button 其他的属性(如 btn.textContent
, btn.style
), 但是有一个特别的地方 ———— 只要监听的事件触发你赋值给他的代码(事件处理函数)就会运行。
语法:
element.on事件名 = function(){};
例子:
const btn = document.querySelector('button');
function bgChange() {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}
// 你可以将一个具名函数赋值给事件处理属性
btn.onclick = bgChange;
注意:
- JavaScript有很多事件处理器属性可供使用,我们会在后面的课程中学习。
- 如果需要取消事件处理器属性的事件处理函数,比如在某些App中当商品售完后购买按钮点击事件将会被取消。只需要将对应的事件处理器属性值赋值为null即可
例子:
const btn = document.querySelector('button');
function bgChange() {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
console.log('我只可以点击一次')
// 取消按钮的点击事件处理函数
btn.onclick = null
}
// 你可以将一个具名函数赋值给事件处理属性
btn.onclick = bgChange;
「课堂练习」
实现点击计数效果
实现点击计数效果
要求:
- 每次点击指定按钮,span中的数字自增一次,
- 当数字自增到10,那么需要重置变量为 0 ,重新开始计数
- 点击按钮,设置按钮被按下时颜色会高亮
补充:
鼠标按下事件为
mousedown
,鼠标松开事件为mouseup
图示效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PjXeLmc6-1627558878765)(http://edu.yueqian.com.cn/group1/M00/05/9F/wKgP3GDvk-CAI9UrAADSeruk4LA016.gif?token=null&ts=null)]
部分代码:
<h3 id ="title">JS基础 - 作业1</h3>
<div class="box">
<span>0</span>
<button >点击一下</button>
</div>
<script>
var btn = document.querySelector("button");
var span = document.querySelector("span");
var num = 0;
//点击事件 鼠标左键 按下并弹起 这才是一个完整的点击事件
// 高亮颜色 #0ab3f8 浅颜色skyblue
btn.onclick = function() {
num++;
if (num > 10) {
num = 0;
}
span.textContent = num;
}
//mousedown事件 鼠标按下事件
btn.onmousedown = function() {
//这里的this 指向了 btn 元素对象.
this.style.backgroundColor = "#0ab3f8";
}
//mouseup鼠标弹起事件.
btn.onmouseup = function() {
this.style.backgroundColor = "skyblue";
}
</script>
「课堂练习」
实现点击爬楼梯效果
实现点击爬楼梯效果
要求:
- 点击按钮,改变索引值
- 根据索引值设置div标签的背景色
- 每点击一次按钮,设置索引值对应的div标签高亮,非索引值对应的移除高亮
- 当5号div标签为高亮背景色,点击按钮后索引值再无法改变
图示效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5EFfX7pC-1627558878772)(http://edu.yueqian.com.cn/group1/M00/05/9F/wKgP3GDvk-CATvGxAACGOHHBSk8023.gif?token=null&ts=null)]
部分代码:
<h3 id ="title">JS基础</h3>
<div >5</div>
<div >4</div>
<div >3</div>
<div >2</div>
<div class="active">1</div>
<button>爬楼梯</button>
<script>
var floor = document.querySelectorAll(".floor");
console.log(floor);
var index = 4; //用来记录楼层 第一层是4
var btn = document.querySelector("button");
btn.onclick = function() {
floor.forEach(function(f, i) { //先循环所有楼层去掉他们的style样式.
f.removeAttribute("style");
});
floor[index].style.backgroundColor = "red"; //设置当前位置的楼层背景为红色.
index--;
if (index < 0) { //如果index小于0楼层爬完
btn.onclick = null; //把事件函数设置为null
}
}
</script>
二、行内事件处理器(请勿使用)
介绍:在Web上注册事件处理程序的最早方法是类似于事件处理器属性的HTML属性(也称为内联事件处理程序)—属性值实际上是当事件发生时要运行的JavaScript代码。
例子:
<button onclick="alert('你好,这是我的旧式事件处理程序!');">Press me</button>
<button onclick="bgChange()">Press me</button>
<script>
function bgChange() {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}
</script>
注意:通过上面的例子,你会发现HTML属性等价于许多事件处理器的属性;但是,你不应该使用这些 —— 他们被认为是不好的做法。使用一个行内事件处理属性似乎看起来很简单,但该用法会导致代码难以管理和效率低下。因为混用 HTML 和 JavaScript,使得文档很难解析,在开发中最好的行为是将 JavaScript 代码单独书写。并且该方法也不适用于给多个元素绑定相同事件处理方法
三、addEventListener() 和removeEventListener()
addEventListener()
介绍:DOM操作提供一种新的事件触发机制, 这个机制给浏览器提供了一个函数 addEventListener()
。该函数和事件处理属性是类似的,但是语法略有不同。我们可以重写上面的随机颜色背景代码
例子:
const btn = document.querySelector('button');
function bgChange() {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}
// 你可以将一个具名函数赋值给事件处理属性
btn.addEventListener('click', bgChange)
这个机制带来了一些相较于旧方式的优点。在大型的、复杂的项目中非常有用并且在其他的一些场景中非常有效。
概念:.addEventListener()
方法将指定的监听器注册到事件目标上,当该对象触发指定的事件时,指定的回调函数就会被执行。 事件目标可以是一个文档上的元素 Element,Document和Window或者任何其他支持事件的对象 (比如 XMLHttpRequest
)。
语法:
target.addEventListener(type, listener);
target.addEventListener(type, listener, options);
target.addEventListener(type, listener, useCapture);
参数
- type:一个区分大小写的字符串,表示要侦听的事件类型。例 “click” 点击事件。
- listener:当指定类型的事件发生时,处理事件目标对象的回调函数。
- options :可选属性,一个指定有关 listener 属性的可选参数对象。可用的选项如下:
- capture: Boolean,表示事件是否使用捕获模式。(事件的捕获会在后面的课程中学习)
- once: Boolean,表示 listener 在添加之后最多只调用一次。如果是 true, listener 会在其被调用之后自动移除。
- passive: Boolean,该属性用来改善的滚屏性能,当值设置为true时,表示 listener 永远不会 阻止浏览器默认事件即永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。(阻止浏览器默认事件会在后面的课程中学习)
- useCapture: Boolean,可选属性表示事件是否使用捕获模式。 默认为
false
。
注意:
addEventListener
与 事件处理器属性不同的是,**addEventListener
**可以给同一个事件监听器注册多个处理器
// 使用事件处理器属性无法给同一个事件同时绑定多个方法, functionB 会覆盖掉 functionA。我们也是通过这种方式取消事件的绑定
myElement.onclick = functionA;
myElement.onclick = functionB;
// 但是addEventListener可以给同一个事件同时绑定多个方法,这时functionA与functionB都会正常工作
myElement.addEventListener('click', functionA);
myElement.addEventListener('click', functionB);
addEventListener
给同一个事件目标注册了多个相同的 EventListener,那么重复的实例会被抛弃。所以这么做不会使得 EventListener 被调用两次,也不需要手动清除多余的EventListener ,因为重复的都被自动抛弃了,但是前提是options中的capture的参数值一致,如果capture的值不一致,此时就算重复定义,也不会被抛弃掉。
function functionA() {
console.log('触发,我会给元素绑定同样的EventListener')
// functionA 被触发又一次给事件目标myElement注册了一个相同的EventListener,因为EventListener完全相同旧的事件监听将会被自动抛弃
myElement.addEventListener('click', functionA);
}
myElement.addEventListener('click', functionA);
function functionB() {
console.log('触发,我会给元素绑定同样的EventListener')
// functionA 被触发又一次给事件目标myElement注册了一个相同的EventListener,但是capture值与旧的EventListener不同,这时就的事件监听将不会被抛弃
myElement.addEventListener('click', functionB, true);
}
myElement.addEventListener('click', functionB);
「课堂练习」
使用addEventListener实现心情便签页面
使用addEventListener实现心情便签页面
要求:
- 点击发表按钮获取输入域的文本
- 把输入域的文本渲染在列标区域
- 把当前发表的时间显示在当前这条消息里面
- 点击“x”标签,直接删除当前这条消息
- 当输入域为空,不允许发表消息变
图示效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZSO06XIy-1627558878782)(http://edu.yueqian.com.cn/group1/M00/05/A0/wKgP3GDvk-CAIwFjAALQp7i8wXg871.gif?token=null&ts=null)]
部分代码:
<style>
ul {
padding: 0;
margin: 0;
list-style: none;
}
p {
padding: 0;
margin: 0;
}
button , input ,textarea {
border: 0;
outline: none;
}
body {
margin: 0;
}
.layout {
width: 560px;
margin: 0 auto;
height: 1000px;
background-color: #f0f0f0;
padding: 20px;
}
.header > div textarea {
/* width: 100%; */
/* 改变盒子大小计算方式 */
/* box-sizing: border-box; */
width: 530px;
height: 60px;
resize: none;
border: 1px solid #ccc;
padding: 10px 15px;
font-size: 20px;
}
.header > div button {
width: 140px;
height: 40px;
background-color: #fff;
cursor: pointer;
float: right;
}
.header > div button:hover {
background-color: skyblue;
}
.clearfix::after {
content: "";
display: block;
clear: both;
}
.list {
background-color: #fff;
margin: 10px 0 0 0 ;
}
.list ul {
padding: 15px 20px;
}
.list ul li {
border: 3px solid #ccc;
padding: 10px 10px;
position: relative;
margin: 0 0 10px 0;
}
.list ul li .txt {
font-size: 18px;
padding: 0 0 10px 0;
}
.list ul li .delete {
position: absolute;
top: 5px;
right: 5px;
cursor: pointer;
width: 30px;
height: 30px;
}
.list ul li .delete:hover {
background-color: #ccc;
}
.list ul li .currentTime {
font-size: 14px;
height: 30px;
line-height: 30px;
}
</style>
<div class="layout">
<div class="header clearfix">
<div>
<textarea id="textarea_box" placeholder="请输入此刻的心情"></textarea>
</div>
<div>
<button id="button_box">发表</button>
</div>
</div>
<div class="list">
<ul id="ul_box">
<!-- <li>
<p class="txt">xxxx</p>
<button class="delete">×</button>
<div class="currentTime">2021-03-09 14:23:59</div>
</li> -->
</ul>
</div>
</div>
<script>
//1).点击发表
var button_box = document.querySelector("#button_box"); //获取按钮元素
var textarea_box = document.querySelector("#textarea_box"); //获取文本域元素
var ul_box = document.querySelector("#ul_box"); //获取列表容器元素
button_box.addEventListener("click", function() { //给按钮添加一个 点击事件监听
var val = textarea_box.value; //获取文本域的值
if (val == "" || val.length <= 0) return alert("内容不能为空..");
var li = document.createElement("li"); //创建li元素
li.innerHTML = '<p class="txt">' + val + '</p><button class="delete" οnclick="deleteLi(this)">×</button><div class="currentTime">' + getNow() + '</div>';
ul_box.insertBefore(li, ul_box.children[0]); //插入节点
textarea_box.value = null; //发表成功后,设置输入文本域的值为空.
});
function deleteLi(el) {
// console.log(el,el.parentElement);// el=deleteLi(this) 代表当前被点击元素对象 el.parentElement 获取el的父元素.
ul_box.removeChild(el.parentElement);
}
//函数 获取当前时间
function getNow() {
var d = new Date();
var year = d.getFullYear();
var month = d.getMonth() + 1;
month = month < 10 ? "0" + month : month;
var date = d.getDate();
date = date < 10 ? "0" + date : date;
var hour = d.getHours();
hour = hour < 10 ? "0" + hour : hour;
var minut = d.getMinutes();
minut = minut < 10 ? "0" + minut : minut;
var sec = d.getSeconds();
sec = sec < 10 ? "0" + sec : sec;
return year + "-" + month + "-" + date + " " + hour + ":" + minut + ":" + sec;
</script>
}
「课堂练习」
面向对象编程开发模拟select元素
要求:
- 创建一个
Select
原型对象- 该原型在实例化时接受id字符串作为参数获取页面中指定元素
Select
原型包含一个原型方法init()
用来初始化页面中的DOM元素实现选择器效果- 点击选择和展示文本元素会显示对应的选择列表
- 点击选择列表的每项,都会将当前被点击项的文本内容展示到展示文本元素上并隐藏选择列表
图示效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zki8R2a4-1627558878785)(http://edu.yueqian.com.cn/group1/M00/05/9F/wKgP3GDvk-CAcf2BAAFNlDQpGXM440.gif?token=null&ts=null)]
部分代码:
<style>
* {
margin: 0;
padding: 0;
list-style: none;
}
.select {
width: 200px;
height: 28px;
border: solid 1px #4a59e7;
margin: 20px auto;
position: relative;
line-height: 28px;
font-size: 14px;
}
.select p {
width: 50px;
height: 28px;
position: absolute;
right: 0;
top: 0;
text-align: center;
color: #FFF;
background: #4a59e7;
cursor: pointer;
}
.select span {
display: block;
width: 150px;
text-indent: 15px;
}
.select ul {
border: solid 1px #4a59e7;
width: 100%;
position: absolute;
left: -1px;
top: 28px;
display: none;
}
.select ul.active {
display: block;
}
.select ul li {
line-height: 27px;
text-indent: 15px;
border-top: solid 1px #4a59e7;
margin-top: -1px;
}
</style>
<div id="box">
<span>请选择城市</span>
<p>选择</p>
<ul>
<li>北京</li>
<li>广州</li>
<li>上海</li>
<li>深圳</li>
<li>厦门</li>
</ul>
</div>
<script>
//补充下列代码
function Select(id) {
this.el=document.getElementById(id);
this.el.className="select"
}
Select.prototype.init = function () {//初始化
var children=this.el.children;
var span=children[0];
var objp=children[1];
var ul=children[2];
var that=this;
//点击span或p元素的时候显示ul
span.addEventListener("click",function(){
//this ==>指向 span
that.showUl();
})
objp.addEventListener("click",function(){
//this ==>指向 objp
that.showUl();
});
//给ul 里面 li绑定点击事件
var lichildren=ul.children;
for(var i=0;i<lichildren.length;i++){
// lichildren[i].addEventListener('click',that.selectLi);
lichildren[i].addEventListener('click',function(){
that.selectLi(this);// this ==> li元素对象
});
}
}
Select.prototype.showUl=function(){
//console.log(this);//this指向了 Select实例
this.el.children[2].className="active"; //找到ul元素 添加 active 让它显示.
}
Select.prototype.selectLi=function(thisparam){
// console.log(this); //this指向事件发生的元素对象 li元素对象
// //找到li元素上级的上级里面第一个子元素 span
// this.parentElement.parentElement.firstElementChild.textContent=this.textContent;
// this.parentElement.className='';//找到当前点击li元素上级元素ul 并设置className='' 隐藏
console.log("==selectLi this=>",this);//this指向了 Select的实例对象. 这时候selectLi是that.selectLi
this.el.firstElementChild.textContent=thisparam.textContent;
thisparam.parentElement.className="";
}
var oSelect = new Select('box');
oSelect.init();
</script>
removeEventListener()
概念:因为addEventListener
可以给同一个事件监听器注册多个处理器,导致我们无法使用覆盖的方式删除已注册的监听事件,所以JavaScript提供了专门用来删除使用.addEventListener()
方法添加的事件。使用事件类型,事件侦听器函数本身,以及可能影响匹配过程的各种可选择的选项的组合来标识要删除的事件侦听器。
语法:
target.removeEventListener(type, listener[, options]);
target.removeEventListener(type, listener[, useCapture]);
参数
- type:一个区分大小写的字符串,表示要侦听的事件类型。例 “click” 点击事件。
- listener:需要从目标事件移除的同一个处理事件目标对象的回调函数。
- options :可选属性,一个用来匹配需要删除指定事件侦听器特征的可选对象。
- capture: Boolean,表示事件是否使用捕获模式。(只有该属性影响匹配)
- useCapture: Boolean,可选属性表示事件是否使用捕获模式。 默认为
false
。
删除指定的事件监听时,需要提供以前调用addEventListener()所提供的监听事件, 这样你或许可以达到移除此监听事件的目的. 并且, 你必须要提供相同的 type 、 listener 、 options 和 useCapture 参数给 removeEventListener()。注意 唯一需要 removeEventListener() 检测的是 capture/useCapture 标志. 这个标志必须与 removeEventListener() 的对应标志匹配, 但是其他的值不需要
例子:
element1.addEventListener("mousedown", handleMouseDown, true);
element1.removeEventListener("mousedown", handleMouseDown, false); // 失败
element1.removeEventListener("mousedown", handleMouseDown, true); // 成功
element2.addEventListener("click", handleMouseDown, { passive: true });
element2.removeEventListener("click", handleMouseDown, { passive: true }); // 成功
element2.removeEventListener("click", handleMouseDown, { capture: false }); // 成功
element2.removeEventListener("click", handleMouseDown, { capture: true }); // 失败
element2.removeEventListener("click", handleMouseDown, { passive: false }); // 成功
element2.removeEventListener("click", handleMouseDown, false); // 成功
element2.removeEventListener("click", handleMouseDown, true); // 失败
ck", handleMouseDown, { passive: true }); // 成功
element2.removeEventListener(“click”, handleMouseDown, { capture: false }); // 成功
element2.removeEventListener(“click”, handleMouseDown, { capture: true }); // 失败
element2.removeEventListener(“click”, handleMouseDown, { passive: false }); // 成功
element2.removeEventListener(“click”, handleMouseDown, false); // 成功
element2.removeEventListener(“click”, handleMouseDown, true); // 失败