目录
DOM事件
HTML DOM允许JS对HTML事件作出反应,当对某个元素进行操作时执行JavaScript代码。
JavaScript与HTML之间的交互是通过事件实现的。事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。
事件的组成:(事件三要素)
- 事件源:事件被触发的对象 --- 按钮对象
- 事件类型:如和触发?触发什么事件?--- 鼠标点击,键盘按下……
- 事件处理程序:通过函数赋值
执行步骤:
获取事件源
绑定事件(注册事件)
采用函数赋值的方式添加事件处理程序
常用事件:
DOM事件流
简单来说,事件流就是事件执行的顺序。DOM树里会有许多元素嵌套,当我们同时给父子元素设置了事件时,父子元素会以一种特定的顺序来执行事件,这就是事件流。并且嵌套的层级不限,事件会贯穿当前元素与根元素。
事件流的分类
DOM支持两种事件流:冒泡型事件流 | 捕获型事件流
- 冒泡型事件流:从特定的事件目标到外层的不特定的元素,由下往上,从叶子节点到根节点。 即:某个具体的元素-> … -> body -> html -> document -> window
- 捕获型事件流:从最外层不特定的事件目标到特定的事件目标,由上往下,从DOM的根节点到叶子节点。即:: window -> document -> html -> body -> … -> 某个具体的元素
冒泡型事件流:
<style>
* {
color: white;
font-size: 20px;
}
#outer {
width: 300px;
height: 300px;
background-color: lightpink;
}
#center {
width: 200px;
height: 200px;
background-color: lightgreen;
}
#inner {
width: 100px;
height: 100px;
background-color: lightskyblue;
}
</style>
</head>
<body>
<div id="outer">
outer
<div id="center">
center
<div id="inner">inner</div>
</div>
</div>
<script>
var inner = document.getElementById("inner");
var center = document.getElementById("center");
var outer = document.getElementById("outer");
// 当我们只有一个inner点击方法的时候 我们发现想要实现的效果和我们预期的一样
inner.onclick = function () {
console.log("我是inner点击的");
};
// 但是当我们给inner的父元素和祖先元素也添加点击事件时 一点击inner 所有祖先元素的事件都会被触发,这就是事件冒泡现象
center.onclick = function () {
console.log("我是center点击的");
};
outer.onclick = function () {
console.log("我是outer点击的");
};
</script>
</body>
第一次只在 inner 上添加了事件,因此点击 inner 时只有 inner 上的事件被触发:
第二次我们在 center 和 outer 上也添加了点击事件,虽然我们只点击了 inner 但是 center 与 outer 上的事件也被触发了:
事实上,这个click事件还会沿着DOM树一直到body,html,document。
阻止事件冒泡
在阻止冒泡之前,我们要知道一个对象叫 event ,它代表事件的状态,比如事件在其中发生的元素,键盘状态,鼠标位置,鼠标按钮状态等……
事件通常与函数结合使用,函数不会在事件发生前被执行!
直接在对应方法中使用 event.stopPropagation() 便可阻止事件冒泡。
注意:如果点击方法时需要同时传递其他参数和event,直接传递event这个单词即可
捕获型事件流
在捕获型事件流中,click事件会首先被document捕获,然后沿着DOM树依次向下,直到事件的目标元素。
由于旧版本浏览器不支持,因此实际当中几乎不会使用事件捕获。通常建议使用事件冒泡,特殊情况下可以使用事件捕获。
DOM事件流的三个阶段
事件捕获、到达目标、事件冒泡
- 捕获阶段:从window对象依次向下传播,到达目标节点,即为捕获阶段。捕获阶段不会响应任何事件
- 目标阶段:在目标节点触发事件,即为目标阶段
- 冒泡阶段:从目标阶段依次向上传播,到达window对象,即为冒泡阶段。
事件处理程序
为响应事件而调用的函数被称为事件处理程序(或事件监听器)。事件处理程序的名字以"on"开头,因此 click 事件的处理程序叫作 onclick,而 load 事件的处理程序叫作 onload。有很多方式可以指定事件处理程序。
HTML事件处理程序
特定元素支持的每个事件都可以使用事件处理程序的名字以 HTML 属性的形式来指定。此时的属性值必须是能够执行的 JavaScript 代码。例如,要在按钮被点击时执行某些 JavaScript 代码,可以使用以下 HTML 属性:
<button onclick="console.log('Clicked')">点我啊</button>
点击这个按钮后,控制台会输出一条消息。
注意,因为属性的值是 JavaScript 代码,所以不能在未经转义的情况下使用 HTML 语法字符,比如和号(&)、双引号(")、小于号(<)和大于号(>)。此时,为了避免使用 HTML 实体,可以使用单引号代替双引号。如果确实需要使用双引号,则要把代码改成下面这样:
<button onclick="console.log("Clicked")">点我啊</button>
在 HTML 中定义的事件处理程序可以包含精确的动作指令,也可以调用在页面其他地方定义的方法。
<!-- 需要加() -->
<button onclick="showMsg()">点我啊</button>
<script>
function showMsg() {
console.log('Hello Wolrd!');
}
</script>
在这个函数中,this 值相当于事件的目标元素,如下面的例子所示:
<button onclick="this.innerHTML = '我被改变了'">点我啊</button>
DOM 0级 事件处理程序 -- 事件不可以追加
要使用 JavaScript 指定事件处理程序,必须先取得要操作对象的引用。每个元素(包括 window 和 document)都有通常小写的事件处理程序属性,比如 onclick。只要把这个属性赋值为一个函数即可:
<button id="btn">点我</button>
<script>
// DOM0
var btn = document.getElementById('btn')
btn.onclick = function(event){
console.log('我被打中了');
console.log(this.id); //btn
}
</script>
点击按钮,这段代码会显示元素的 ID。这个 ID 是通过 this.id 获取的。不仅仅是 id,在事件处理程序里通过 this 可以访问元素的任何属性和方法。以这种方式添加事件处理程序是注册在事件流的冒泡阶段的。在DOM2 中也如此。
移除事件程序:
btn.onclick = null; // 移除事件处理程序
把事件处理程序设置为 null,再点击按钮就不会执行任何操作了。
DOM 2级 事件处理程序 -- 事件可以追加
DOM2 Events 为事件处理程序的赋值和移除定义了两个方法:
addEventListener()和removeEventListener()
这两个方法暴露在所有 DOM 节点上,它们接收 3 个参数:事件名、事件处理函数和一个布尔值,true 表示在捕获阶段调用事件处理程序,false(默认值)表示在冒泡阶段调用事件处理程序。
<button id="btn">点我</button>
<script>
var btn = document.getElementById('btn')
// 单纯添加事件处理程序
btn.addEventListener('click',function(){
console.log('怎么又被打中了');
},false)
btn.addEventListener('click',function(){
console.log(this.id); // btn
})
</script>
这里给按钮添加了两个事件处理程序。多个事件处理程序以添加顺序来触发,因此前面的代码会先打印'我被点击了',然后显示元素ID。
通过 addEventListener()添加的事件处理程序只能使用 removeEventListener()并传入与添加时同样的参数来移除。这意味着使用 addEventListener()添加的匿名函数无法移除,如:
<button id='btn'>点我啊</button>
<script>
var btn = document.getElementById("btn");
btn.addEventListener("click", function () {
console.log('我被点击了');
}, false);
// 移除
btn.removeEventListener("click", function () {
console.log(this.id); //没有效果
})
</script>
解绑事件要具名:
这个例子通过 addEventListener()添加了一个匿名函数作为事件处理程序。然后,又以看起来相同的参数调用了 removeEventListener()。但实际上,第二个参数与传给 addEventListener()的完全不是一回事。传给 removeEventListener()的事件处理函数必须与传给 addEventListener()的是同一个,如:
<button id='btn'>点我啊</button>
<script>
var btn = document.getElementById("btn");
// 新增一个方法
var handler = function () {
console.log(this.id);
}
btn.addEventListener("click", handler, false);
// 移除
btn.removeEventListener("click", handler, false); // 有效果
</script>
这个例子有效,因为调用 addEventListener()和 removeEventListener()时传入的是同一个函数。
事件对象
在 DOM 中发生事件时,所有相关信息都会被收集并存储在一个名为 event 的对象中。这个对象包含了一些基本信息,比如导致事件的元素、发生的事件类型,以及可能与特定事件相关的任何其他数据。例如,鼠标操作导致的事件会生成鼠标位置信息,而键盘操作导致的事件会生成与被按下的键有关的信息。所有浏览器都支持这个 event 对象,尽管支持方式不同。
注意:event 对象只在事件处理程序执行期间存在,一旦执行完毕,就会被销毁。
阻止默认事件发生
preventDefault()方法
用于阻止特定事件的默认动作。比如,链接的默认行为就是在被单击时导航到 href 属性指定的 URL或是修改表单提交的默认事件。如果想阻止这些行为,可以在 onclick 事件处理程序中取消,如:
<form action="./1-class01.html">
<button id="btn">提交</button>
</form>
<script>
// 组织a事件的默认事件发生
var a = document.getElementsByTagName('a')[0]
a.onclick = function(event){
event.preventDefault();
console.log('a被点中了');
}
var f = document.getElementById('btn')
btn.onclick = function(event){
event.preventDefault();
console.log('┗|`O′|┛ 嗷~~');
}
</script>
事件委托和事件代理
事件委托:也就是事件代理,也就是将原本绑定在子元素身上的事件 委托 给父元素。让父元素去监听事件。其原理是利用事件冒泡。这意味着可以为整个页面指定一个 onclick 事件处理程序,而不用为每个可点击元素分别指定事件处理程序。
优点:大大减少DOM操作和浏览器的重排和重绘,节省内存占用。也可以实现当新增对象时无需再次对其绑定事件。
如·:
这里的 HTML 包含 3 个列表项,在被点击时应该执行某个操作。对此,通常的做法是像这样指定 3个事件处理程序:
使用事件委托,只要给所有元素共同的祖先节点添加一个事件处理程序,就可以解决代码大段雷同的问题:
这里只给<ul id="father">元素添加了一个 onclick 事件处理程序。因为所有列表项都是这个元素的后代,所以它们的事件会向上冒泡,最终都会由这个函数来处理。但事件目标是每个被点击的列表项,只要检查 event 对象的 id 属性就可以确定,然后再执行相应的操作即可。相对于前面不使用事件委托的代码,这里的代码不会导致先期延迟,因为只访问了一个 DOM 元素和添加了一个事件处理程序。结果对用户来说没有区别,但这种方式占用内存更少。所有使用按钮的事件(大多数鼠标事件和键盘事件)都适用于这个解决方案。