1、元素绑定事件
(1)GlobalEventHandlers.eventType
element.onclick = functionRef;
全局事件处理器(GlobalEventHandlers)的 eventType 属性,是处理当前元素的 eventType 事件的事件处理器(EventHandler)。
- functionRef:一个函数名称,或一个函数表达式。该函数接收 MouseEvent 对象作为其唯一参数。在函数内,this 是触发当前事件的元素。
(2)EventTarget.addEventListener()
target.addEventListener(type, listener[, options]);
target.addEventListener(type, listener[, useCapture]);
// Gecko/Mozilla only
target.addEventListener(type, listener[, useCapture, wantsUntrusted ]);
将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。 事件目标可以是一个文档上的元素 Element,Document和Window或者任何其他支持事件的对象 (比如 XMLHttpRequest)。
- type:表示监听事件类型的字符串;
- listener:当所监听的事件类型触发时,会接收到一个事件通知(实现了 Event 接口的对象)对象。listener 必须是一个实现了 EventListener 接口的对象,或者是一个函数;
- options:可选,一个指定有关 listener 属性的可选参数对象;
- useCapture:可选,指定需要移除的 EventListener 函数是否为捕获监听器。如果无此参数,默认值为 false;
- wantsUntrusted :可选,如果为 true , 则事件处理程序会接收网页自定义的事件。
(3)绑定事件的兼容代码
封装事件绑定函数,兼容不同的浏览器(有些浏览器不支持addEventListener(),而使用的是attachEvent(),比如IE8)。
<body>
<input type="button" value="btn" id="btn">
<script>
function addEventListener(element, type, fn){
if(element.addEventListener){
element.addEventListener(type,fn, false);
}else if(element.attachEvent){
// attachEvent传入类型时,需要在前面加 "on"
element.attachEvent("on" + type, fn);
}else{
// 两个函数都不支持时,直接绑定
element["on"+type] = fn;
}
}
addEventListener(document.getElementById("btn"), "click", function(){alert("Hi!");});
</script>
</body>
2、元素解绑事件
用什么方式绑定事件,就应该用对应的方式解绑事件。
(1)GlobalEventHandlers.eventType
element.onclick = null;
示例代码:
简单的点击按钮弹框事件绑定,与点击按钮解除绑定事件。
<body>
<input type="button" value="btn1" id="btn1">
<input type="button" value="Unbind event" id="btn2">
<script>
document.getElementById("btn1").onclick = function(){
alert("Hi!");
}
document.getElementById("btn2").onclick = function(){
document.getElementById("btn1").onclick = null;
}
</script>
</body>
(2)EventTarget.removeEventListener()
target.removeEventListener(type, listener[, options]);
target.removeEventListener(type, listener[, useCapture]);
- type:一个字符串,表示需要移除的事件类型,如 “click”;
- listener:需要从目标事件移除的 EventListener 函数;
- options:可选,一个指定事件侦听器特征的可选对象;
- useCapture:可选,指定需要移除的 EventListener 函数是否为捕获监听器。如果无此参数,默认值为 false。
示例代码:
注意,使用这种方式解绑的话,绑定时不宜使用匿名函数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<input type="button" value="btn1" id="btn1">
<input type="button" value="Unbind event" id="btn2">
<script>
function f1(){
alert("Hi!");
}
document.getElementById("btn1").addEventListener("click", f1, false);
document.getElementById("btn2").onclick = function(){
document.getElementById("btn1").removeEventListener("click", f1);
}
</script>
</body>
</html>
(3)解绑事件的兼容代码
封装事件解绑函数,兼容不同的浏览器(有些浏览器不支持removeEventListener(),而使用的是detachEvent(),比如IE8)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<input type="button" value="btn1" id="btn1">
<input type="button" value="Unbind event" id="btn2">
<script>
function addEventListener(element, type, fn){
if(element.addEventListener){
element.addEventListener(type,fn, false);
}else if(element.attachEvent){
// attachEvent传入类型时,需要在前面加 "on"
element.attachEvent("on" + type, fn);
}else{
element["on"+type] = fn;
}
}
function removeEventListener(element, type, fn){
if(element.removeEventListener){
element.removeEventListener(type,fn, false);
}else if(element.detachEvent){
// detachEvent传入类型时,需要在前面加 "on"
element.detachEvent("on" + type, fn);
}else{
element["on"+type] = null;
}
}
function f(){alert("Hi!")}
addEventListener(document.getElementById("btn1"), "click", f);
document.getElementById("btn2").onclick = function(){
removeEventListener(document.getElementById("btn1"), "click", f)
}
</script>
</body>
</html>
3、事件的三个阶段
(1)三个阶段介绍
当一个DOM事件被触发时,不只是在本身对象触发一次,而是会经历三个不同的阶段。
- 捕获阶段:由外到内进行事件传播,事件从根节点流向目标节点,途中流经各个DOM节点,在各个节点上触发捕获事件,直到达到目标节点;
- 目标(target)阶段:在此阶段中,事件传导到目标节点;
- 事件冒泡: 由内到外进行事件传播,从目标事件位置往文档的根节点方向回溯。
(2)Event.eventPhase
表示事件流当前处于哪一个阶段,返回一个代表当前执行阶段的整数值。
var phase = event.eventPhase;
- Event.NONE(0,事件未处理);
- Event.CAPTURING_PHASE(1,事件捕获);
- Event.AT_TARGET(2,目标阶段);
- Event.BUBBLING_PHASE(3,冒泡阶段)。
(3)事件阶段控制
在addEventListener()函数中,传入的第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。
4、阻止事件冒泡
在事件冒泡阶段,会由内到外进行事件传播,直到根节点。也就是说,在多个嵌套的元素都注册了相同的事件时,如果里面元素的事件触发了,那么外层的相同事件也会随之触发。
(1)阻止事件冒泡的方式
- event.stopPropagation()
event.stopPropagation();
阻止捕获和冒泡阶段中当前事件的进一步传播。
- event.cancelBubble(已废弃)
event.cancelBubble = true;
// 某些浏览器在事件触发时,不会在事件处理函数中自动传入一个事件参数
window.event.cancelBubble = true;
获取或设置一个布尔值,表明当前事件是否要取消冒泡。
(2)阻止事件冒泡的兼容代码
有的浏览器不支持stopPropagation()方法,还有的浏览器在事件处理函数中不传入事件处理参数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
#div1{
width: 200px;
height: 200px;
background-color: green;
}
#div2{
width: 100px;
height: 100px;
background-color: yellow;
}
</style>
</head>
<body>
<div id="div1">
<div id="div2"></div>
</div>
<script>
function stopPropagation(e){
if(e.stopPropagation){
e.stopPropagation();
}else if(e){
// 有事件处理参数传入,没有stopPropagation()方法
e.cancelBubble = true;
}else{
// 没有事件处理参数传入
window.event.cancelBubble = true;
}
}
document.getElementById("div1").onclick = function(){console.log("div1");};
document.getElementById("div2").onclick = function(e){
console.log("div2");
stopPropagation(e);
}
</script>
</body>
</html>
5、综合案例
实现一个在文本输入时,带文本提示功能的搜索框。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
background: #A3D0C3;
font-weight: 500;
font-family: "Microsoft YaHei","宋体","Segoe UI", "Lucida Grande",
Helvetica, Arial,sans-serif, FreeSans, Arimo;
}
div.search {
padding: 30px 0;
}
form {
position: relative;
width: 300px;
margin: 0 auto;
}
input, button {
border: none;
outline: none;
}
input {
width: 100%;
height: 42px;
padding-left: 13px;
}
button {
height: 42px;
width: 42px;
cursor: pointer;
position: absolute;
}
/*搜索框*/
.bar1 input {
border: 2px solid #7BA7AB;
border-radius: 5px;
background: #F9F0DA;
color: #9E9C9C;
}
.bar1 button {
top: 0;
right: 0;
background: #7BA7AB;
border-radius: 0 5px 5px 0;
}
.bar1 button:before {
content: "\f002";
font-family: FontAwesome;
font-size: 16px;
color: #F9F0DA;
}
</style>
</head>
<body>
<div class="search bar1" id="div1">
<form>
<input type="text" placeholder="请输入您要搜索的内容..." id="text1">
<button type="submit"></button>
</form>
</div>
<script>
var contents = ["Apple", "Alice", "Abc", "Bob", "Bbq"];
// 绑定的是键盘抬起事件
document.getElementById("text1").onkeyup = function(){
// 每次事件开始,先把原来的div删除
if(document.getElementById("div2")){
document.getElementById("div1").removeChild(document.getElementById("div2"));
}
var value = this.value;
// 临时数组,存储提示文字数据
var tempArr = [];
// 构建提示数据
for(var i = 0; i < contents.length; i++){
if(contents[i].indexOf(value) == 0){
tempArr.push(contents[i]);
}
}
// 如果未匹配提示内容或输入为空,则直接返回
if(tempArr.length == 0 || value.length == 0){
return;
}
// 新建div元素
var divObj = document.createElement("div");
divObj.id = "div2";
divObj.style.border = "1px solid #7BA7AB";
divObj.style.width = "300px";
divObj.style.margin = "0 auto";
// 向div添加内容
for(var i = 0; i < tempArr.length; i++){
var pObj = document.createElement("p");
pObj.innerText = tempArr[i];
pObj.style.marginLeft = "10px";
divObj.appendChild(pObj);
}
// 将新建的div放到网页中
document.getElementById("div1").appendChild(divObj);
}
</script>
</body>
</html>