跳到主要内容

事件处理函数

事件

事件是元素固有的特性或功能,已经预先定义好。

绑定事件

绑定事件即关联事件处理函数,当事件触发时执行相应的函数。

事件句柄绑定

将事件绑定到事件源 divElement 上,onclick 是事件句柄,事件句柄绑定了事件处理函数。

divElement.onclick = function () {
this.style.backgroundColor = 'orange';
};

绑定事件处理函数

句柄绑定

同一元素只能赋值一次。多次赋值会覆盖前一次绑定的事件处理函数。

// 句柄绑定
element.onclick = function () {};
// 第二次绑定会覆盖第一次
element.onclick = function () {};

行内事件绑定

不推荐使用行内事件绑定,最好将结构与逻辑分离。

<div onclick="test()"></div>
<div onclick="console.log('1')"></div>

注册事件监听器

addEventListener 是 W3C 标准,IE9 以下不支持。它允许为同一元素绑定多个事件处理函数。

// 第一个事件
divElement.addEventListener(
'click',
function () {
console.log(1);
},
false
);
// 第二个事件
divElement.addEventListener(
'click',
function () {
console.log(2);
},
false
);

绑定事件监听器

绑定外部函数时,如果使用相同的函数引用,仅会执行一次。

// 两次绑定使用相同的引用,仅执行一次
divElement.addEventListener('click', handleClick, false);
divElement.addEventListener('click', handleClick, false);

function handleClick() {
console.log(1);
}

IE8 以下的事件绑定

在 IE8 及以下版本中,可以使用 attachEvent 绑定事件,但 this 指向 window

divElement.attachEvent('onclick', function () {
// this 指向 window
console.log(1);
});

批量绑定事件

为多个元素批量绑定事件,可以使用闭包或立即执行函数表达式 (IIFE) 来保持变量的正确引用。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script type="text/javascript">
var listItems = document.getElementsByTagName('li'),
len = listItems.length;

for (var i = 0; i < len; i++) {
(function (index) {
var item = listItems[index];
item.addEventListener(
'click',
function () {
console.log(index);
},
false
);
})(i);
}
</script>
</body>
</html>

不同运行环境中的 this

事件处理程序在不同运行环境下,this 指向不同。

var button = document.getElementsByTagName('button')[0];

button.onclick = function () {
console.log(this); // button 元素
};

button.addEventListener(
'click',
function () {
console.log(this); // button 元素
},
false
);

button.attachEvent('onclick', function () {
console.log(this); // window
});

封装事件处理

通过封装事件处理函数,可以兼容不同浏览器的事件绑定方式。

function addEvent(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, function () {
handler.call(element);
});
} else {
element['on' + type] = handler;
}
}

addEvent(button, 'click', function () {
console.log(1);
});

移除绑定的事件

移除事件处理函数时,确保传入的函数引用相同。

button.onclick = null;
button.onclick = false;

button.addEventListener('click', handleClick, false);
// 移除的函数引用必须相同
button.removeEventListener('click', handleClick, false);

button.detachEvent('onclick', handleClick);

// 函数内部移除事件处理,但在严格模式下不可使用
button.addEventListener(
'click',
function () {
console.log(1);
this.removeEventListener('click', arguments.callee, false);
},
false
);

内联元素无法嵌套

在 HTML 中,某些内联元素(如 <a> 标签)不应嵌套其他内联元素,以避免布局和功能问题。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<style>
a {
display: block;
}

[href="http://www.baidu.com"]
{
width: 100px;
height: 100px;
background-color: green;
}

[href="http://www.taobao.com"]
{
width: 100px;
height: 100px;
background-color: orange;
}
</style>

<body>
<a href="http://www.baidu.com">
百度
<a href="http://www.taobao.com">淘宝</a>
</a>
</body>
</html>

事件冒泡

在嵌套的元素中,点击 inner 元素时,事件会首先触发 inner,然后逐级向上传递到父级元素。前提是事件类型相同。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<style>
.wrapper {
width: 300px;
height: 300px;
background-color: green;
}

.outer {
width: 200px;
height: 200px;
background-color: pink;
}

.inner {
width: 100px;
height: 100px;
background-color: orange;
}
</style>

<body>
<div class="wrapper">
<div class="outer">
<div class="inner"></div>
</div>
</div>
<script>
var wrapper = document.getElementsByClassName('wrapper')[0],
outer = wrapper.getElementsByClassName('outer')[0],
inner = outer.getElementsByClassName('inner')[0];

wrapper.addEventListener(
'click',
function () {
console.log('wrapper');
},
false
);

outer.addEventListener(
'click',
function () {
console.log('outer');
},
false
);

inner.addEventListener(
'click',
function () {
console.log('inner');
},
false
);
</script>
</body>
</html>

取消事件冒泡

使用 e.stopPropagation() 可以阻止事件冒泡,IE 中使用 e.cancelBubble = true

wrapper.addEventListener(
'click',
function () {
console.log('bubbleWrapper');
},
false
);

outer.addEventListener(
'click',
function () {
console.log('bubbleOuter');
},
false
);

inner.addEventListener(
'click',
function (e) {
var event = e || window.event;
// 阻止事件冒泡
event.stopPropagation();
// IE 下
// event.cancelBubble = true;
console.log('bubbleInner');
},
false
);

封装 cancelBubble 方法

为兼容不同浏览器,封装一个通用的 cancelBubble 方法。

function cancelBubble(e) {
var event = e || window.event;
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}

事件捕获

在嵌套的元素中,点击 inner 元素时,事件会首先触发 wrapper,然后传递到 outerinner。前提是事件类型相同。

wrapper.addEventListener(
'click',
function () {
console.log('wrapper');
},
true
);

outer.addEventListener(
'click',
function () {
console.log('outer');
},
true
);

inner.addEventListener(
'click',
function () {
console.log('inner');
},
true
);

捕获和冒泡同时存在

事件处理顺序为先捕获,后冒泡。事件源的执行顺序按照绑定的先后顺序。

wrapper.addEventListener(
'click',
function () {
console.log('bubbleWrapper');
},
false
);

outer.addEventListener(
'click',
function () {
console.log('bubbleOuter');
},
false
);

inner.addEventListener(
'click',
function () {
console.log('bubbleInner');
},
false
);

wrapper.addEventListener(
'click',
function () {
console.log('wrapper');
},
true
);

outer.addEventListener(
'click',
function () {
console.log('outer');
},
true
);

inner.addEventListener(
'click',
function () {
console.log('inner');
},
true
);

不支持捕获的事件

以下事件不支持冒泡和捕获:

  • focus
  • blur
  • change
  • submit
  • reset
  • select

此外,IE 浏览器不支持事件捕获。

取消默认事件

仅在事件句柄绑定方式中使用,可以阻止右键菜单的出现等默认行为。

document.oncontextmenu = function (e) {
var event = e || window.event;
// W3C 标准
event.preventDefault();
// IE9 以下
event.returnValue = false;
// 常用方式
return false;
};

阻止 <a> 标签的默认行为

通过阻止 <a> 标签的默认行为,可以控制链接的跳转。

<!-- 锚点 点击回到某个位置 -->
<a href="#"></a>
<!-- void(0) -> 返回0 -->
<a href="javascript:void(0);"></a>
<!-- 不刷新/不跳转 -->
<a href="javascript:;"></a>
<!-- 跳转 -->
<a href="http://www.baidu.com"></a>
<a href="">跳转</a>
<script>
var anchor = document.getElementsByTagName('a')[0];
anchor.onclick = function (e) {
e.preventDefault();
};
</script>

点击图标查看详情

阻止 inner 元素的默认跳转事件,但不阻止外部元素的默认事件。这种场景在实际业务中很常见。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<style>
.link {
display: block;
width: 100px;
height: 100px;
background-color: orange;
}
</style>

<body>
<a href="http://www.baidu.com" class="link" target="_blank">
<div class="inner">点击DIV</div>
</a>
<script>
var inner = document.getElementsByClassName('inner')[0];
inner.onclick = function (e) {
var event = e || window.event;
event.preventDefault();
console.log('我点击了');
};
</script>
</body>
</html>

阻止表单提交

取消页面跳转的提交现象,阻止默认行为后,可以进行异步提交。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>

<body>
<form action="">
<input type="text" name="content" />
<input type="submit" value="提交" id="submit" />
</form>
<script>
var submitButton = document.getElementById('submit');
submitButton.onclick = function (e) {
var event = e || window.event;
event.preventDefault();
console.log('提交了');
};
</script>
</body>
</html>

内联元素无法嵌套

在 HTML 中,某些内联元素(如 <a> 标签)不应嵌套其他内联元素,以避免布局和功能问题。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<style>
a {
display: block;
}

[href="http://www.baidu.com"]
{
width: 100px;
height: 100px;
background-color: green;
}

[href="http://www.taobao.com"]
{
width: 100px;
height: 100px;
background-color: orange;
}
</style>

<body>
<a href="http://www.baidu.com">
百度
<a href="http://www.taobao.com">淘宝</a>
</a>
</body>
</html>

事件冒泡

在嵌套的元素中,点击 inner 元素时,事件会首先触发 inner,然后逐级向上传递到父级元素。前提是事件类型相同。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<style>
.wrapper {
width: 300px;
height: 300px;
background-color: green;
}

.outer {
width: 200px;
height: 200px;
background-color: pink;
}

.inner {
width: 100px;
height: 100px;
background-color: orange;
}
</style>

<body>
<div class="wrapper">
<div class="outer">
<div class="inner"></div>
</div>
</div>
<script>
var wrapper = document.getElementsByClassName('wrapper')[0],
outer = wrapper.getElementsByClassName('outer')[0],
inner = outer.getElementsByClassName('inner')[0];

wrapper.addEventListener(
'click',
function () {
console.log('wrapper');
},
false
);

outer.addEventListener(
'click',
function () {
console.log('outer');
},
false
);

inner.addEventListener(
'click',
function () {
console.log('inner');
},
false
);
</script>
</body>
</html>

取消事件冒泡

使用 e.stopPropagation() 可以阻止事件冒泡,IE 中使用 e.cancelBubble = true

wrapper.addEventListener(
'click',
function () {
console.log('bubbleWrapper');
},
false
);

outer.addEventListener(
'click',
function () {
console.log('bubbleOuter');
},
false
);

inner.addEventListener(
'click',
function (e) {
var event = e || window.event;
// 阻止事件冒泡
event.stopPropagation();
// IE 下
// event.cancelBubble = true;
console.log('bubbleInner');
},
false
);

封装 cancelBubble 方法

为兼容不同浏览器,封装一个通用的 cancelBubble 方法。

function cancelBubble(e) {
var event = e || window.event;
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}

事件捕获

在嵌套的元素中,点击 inner 元素时,事件会首先触发 wrapper,然后传递到 outerinner。前提是事件类型相同。

wrapper.addEventListener(
'click',
function () {
console.log('wrapper');
},
true
);

outer.addEventListener(
'click',
function () {
console.log('outer');
},
true
);

inner.addEventListener(
'click',
function () {
console.log('inner');
},
true
);

捕获和冒泡同时存在

事件处理顺序为先捕获,后冒泡。事件源的执行顺序按照绑定的先后顺序。

wrapper.addEventListener(
'click',
function () {
console.log('bubbleWrapper');
},
false
);

outer.addEventListener(
'click',
function () {
console.log('bubbleOuter');
},
false
);

inner.addEventListener(
'click',
function () {
console.log('bubbleInner');
},
false
);

wrapper.addEventListener(
'click',
function () {
console.log('wrapper');
},
true
);

outer.addEventListener(
'click',
function () {
console.log('outer');
},
true
);

inner.addEventListener(
'click',
function () {
console.log('inner');
},
true
);

不支持捕获的事件

以下事件不支持冒泡和捕获:

  • focus
  • blur
  • change
  • submit
  • reset
  • select

此外,IE 浏览器不支持事件捕获。

取消默认事件

仅在事件句柄绑定方式中使用,可以阻止右键菜单的出现等默认行为。

document.oncontextmenu = function (e) {
var event = e || window.event;
// W3C 标准
event.preventDefault();
// IE9 以下
event.returnValue = false;
// 常用方式
return false;
};

阻止 <a> 标签的默认行为

通过阻止 <a> 标签的默认行为,可以控制链接的跳转。

<!-- 锚点 点击回到某个位置 -->
<a href="#"></a>
<!-- void(0) -> 返回0 -->
<a href="javascript:void(0);"></a>
<!-- 不刷新/不跳转 -->
<a href="javascript:;"></a>
<!-- 跳转 -->
<a href="http://www.baidu.com"></a>
<a href="">跳转</a>
<script>
var anchor = document.getElementsByTagName('a')[0];
anchor.onclick = function (e) {
e.preventDefault();
};
</script>

点击图标查看详情

阻止 inner 元素的默认跳转事件,但不阻止外部元素的默认事件。这种场景在实际业务中很常见。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<style>
.link {
display: block;
width: 100px;
height: 100px;
background-color: orange;
}
</style>

<body>
<a href="http://www.baidu.com" class="link" target="_blank">
<div class="inner">点击DIV</div>
</a>
<script>
var inner = document.getElementsByClassName('inner')[0];
inner.onclick = function (e) {
var event = e || window.event;
event.preventDefault();
console.log('我点击了');
};
</script>
</body>
</html>

阻止表单提交

取消页面跳转的提交现象,阻止默认行为后,可以进行异步提交。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>

<body>
<form action="">
<input type="text" name="content" />
<input type="submit" value="提交" id="submit" />
</form>
<script>
var submitButton = document.getElementById('submit');
submitButton.onclick = function (e) {
var event = e || window.event;
event.preventDefault();
console.log('提交了');
};
</script>
</body>
</html>