一、JS中的DOM事件模型
JS中有两种事件模型:
- DOM 0级事件模型 (几乎所有浏览器都支持)
- DOm 2级事件模型(现代浏览器几乎都支持,IE8之前的不支持)
(一)DOM 0级事件模型
有两种形式:
//第一种:html标签中添加事件
<input type="button" onclick="doSomething(...)"/>
//第二种:dom对象绑定事件
input.onclick=function(){...}
缺点:只能添加一个相同的事件,之后添加的会覆盖前面的。
input.onclick=function(){alert(1);} //被覆盖了,不会执行
input.onclick=function(){alert(2);} //之后的dom点击事件会覆盖前面的。
(二)事件冒泡
点击子元素,其父元素的事件处理模型也都被执行了。
//点击<p>标签
<body>
<div id="div1">
<div id="div1-1">
<div id="div1-1-1">
<div id="div1-1-1-1">
<p id="p1">
事件冒泡测试
</p>
</div>
</div>
</div>
</div>
<script type="text/javascript">
document.getElementById('p1').onclick=function(event){console.log('p1');};
document.getElementById('div1').onclick=function(event){console.log('div1');};
document.getElementById('div1-1').onclick=function(event){console.log('div1-1');};
document.getElementById('div1-1-1').onclick=function(event){console.log('div1-1-1');};
document.getElementById('div1-1-1-1').onclick=function(event){console.log('div1-1-1-1');};
//结果为从内到外以此触发事件,vue中也会遇到
//p1
//div1-1-1-1
//div1-1-1
//div1-1
//div1
</script>
</body>
取消事件冒泡
- event.stopPropagation();
- event.cancelBubble = true;
(三)DOM 2级事件模型
DOM 2级事件模型弥补了DOM 0级事件缺陷,可以绑定多个相同事件。
同时也拥有事件捕获和事件冒泡的功能(事件捕获:从祖先元素捕获到子元素,和冒泡相反)
事件捕获:从祖先元素向下传播
事假冒泡:从子元素向上传播
DOM2注册事件处理函数:
addEventListenter(eventType,listener,useCapture)
- eventType:事件类型(click、focus、blur)不带on
- listener:事件处理函数,可以是匿名函数
- useCapture:默认值是false,只冒泡不捕获;true只捕获不冒泡(捕获很少用)。
attachEvent(eventName,handler)(IE)
老的IE浏览器使用attachEvent添加事件处理函数
- eventName:事件名称(需要加上on)
- handler:事件处理函数
var event = event || window.event;
var target = event.target || event.srcElement;
使用原生JS绑定DOM事件需要兼容各种浏览器的写法
var EventsUtility = {
addEvent: function (element, type, callback) {
if (typeof addEventListener !== 'undefined') { //DOM 2级事件
element.addEventListener(type, callback, false);
} else if (typeof attachEvent !== 'undefined') {
element.attachEvent('on' + type, callback); //DOM 2级事件,兼容IE
} else { //DOM 0级事件,兼容低版本浏览器
element['on' + type] = callback;
}
},
removeEvent: function (element, type, callback) {
if (typeof removeEventListener !== 'undefined') { //DOM 2级事件
element.removeEventListener(type, callback, false);
} else if (typeof detachEvent !== 'undefined') {
element.detachEvent('on' + type, callback); //DOM 2级事件,兼容IE
} else { //DOM 0级事件,兼容低版本浏览器
element['on' + type] = null;
}
},
}
二、jQuery事件模型
特点:
- 提供统一的事件处理方法
- 内部采用了DOM2级事件模型,允许添加多个事件处理函数
- 使用标准事件名称,不带on
- event事件实例作为事件处理函数的第一个参数
- 标准化了事件常用属性,解决了兼容性问题 比如target
- 提供了统一的事件取消和阻止默认行为的方法
(一)添加DOM事件处理
.on(eventType[,selector][,data][,handler]) //推荐使用,其他的方法不推荐
$('XXX').on(eventType[,select][,data],function(event){
//event.target得到的是事件发生元素
})
- eventType 事件类型,多个事件可以用空格分隔
- selector表示一个选择器 指定事件注册的后代(用于事件委托)
- data 表示传递的数据,可以在函数中使用,通过event.data获取
- 表示事件处理函数
- 可以使用链式语法添加多个事件
事件委托
如果要为大量的元素绑定事件的话,为每个元素都绑定一个事件效率会很低。使用事件委托可以提高效率。
事件委托,利用事件冒泡的原理,把子元素的相同的事件委托给父元素,子元素上发生的某个事件会从父元素哪里得到具体的事件处理程序。
- 此时event.target是发生事件的子元素
- 上面的selector可以指定哪些子元素需要委托此事件
- 新添加的符合selector的子元素默认就委托了此事件
//新闻列表数量太多,每一个都绑定事件效率低,而且还会有新加
<body>
<div class="new">
<p class="new-li p1">新闻段落1</p>
<p class="new-li p2">新闻段落2</p>
<p class="new-li p3">新闻段落3</p>
<p class="new-li p4">新闻段落4</p>
<p class="new-li p5">新闻段落5</p>
<p class="new-li p6">新闻段落6</p>
<p class="new-li p7">新闻段落7</p>
<p class="new-li p8">新闻段落8</p>
<p class="new-li p9">新闻段落9</p>
<p class="new-li p10">新闻段落10</p>
<div class="new-li-page">事件
这是分页,不会触发
</div>
</div>
<script type="text/javascript" src="//cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script type="text/javascript">
$(function(){
//为父元素绑定事件处理,利用事件委托,将指定子元触发的事件委托给父元素处理
$('.new').on('click','p',{title: '新闻标题'},function(event){
console.log($(event.target).text());
});
});
</script>
</body>
统一方法和属性
- 事件冒泡:stopPropagation()
- 阻止默认行为:preventDefault()
- 同时阻止冒泡和默认行为:return false
所有支持的事件
事件名 | 说明 | 事件名 | 说明 |
---|---|---|---|
blur | focusout | ||
change | keydown | ||
click | keypress | ||
dblclick | keyup | ||
error | load | ||
focus | unload | ||
focusin | |||
mousedown | mouseup | ||
mouseenter | ready | ||
mouseleave | resize | ||
mousemove | scroll | ||
mouseout | select | ||
mouseover | submit |
一次性的事件处理
one(eventType[,selector][,data],handler)
这个方法注册的事件执行一次后就被销毁了。
jQuery不支持双击事件!jQuery不支持双击事件!!jQuery不支持双击事件!!!
(二)移除DOM事件处理
off(eventType[,selector][,handler])
- eventType:事件类型,可以使用空格分开的多个类型
- selector:事件目标
- handler:事件处理函数,和removeEventListener()一样,必须是不带括号的函数名,如果没有参数,则删除所有类型的事件处理
1.删除一个事件处理
<script src="//cdn.bootcss.com/jquery/1.12.4/jquery.js"></script>
<script>
$(function () {
var click1 = function(event){
$(event.target).css('text-indent','1px');
}
var click2 = function(event){
console.log($(event.target).text());
}
var click3 = function(event){
console.log('clicked');
}
var mouseover1 = function(){
console.log('mouseover');
}
$('div.cards').on('click','div',click1)
.on('click','div',click2)
.on('click','div',click3);
$('div.cards').off('click','div',click1); //移除click1点击事件
});
</script>
2.移除同类型事件处理
<script>
$(function () {
var click1 = function(event){
$(event.target).css('text-indent','1px');
}
var click2 = function(event){
console.log($(event.target).text());
}
var click3 = function(event){
console.log('clicked');
}
var mouseover1 = function(){
console.log('mouseover');
}
$('div.cards').on('click','div',click1)
.on('click','div',click2)
.on('click','div',click3)
.on('mouseover','div',mouseover1);
$('div.cards').off('click'); //移除所有click点击事件
});
</script>
3.移除多个类型的事件
<script>
$(function () {
var click1 = function(event){
$(event.target).css('text-indent','1px');
}
var click2 = function(event){
console.log($(event.target).text());
}
var click3 = function(event){
console.log('clicked');
}
var mouseover1 = function(){
console.log('mouseover');
}
$('div.cards').on('click','div',click1)
.on('click','div',click2)
.on('click','div',click3)
.on('mouseover','div',mouseover1);
$('div.cards').off('click mouseover'); //移除多个事件处理
});
</script>
4.移除元素上所有事件
<script>
$(function () {
var click1 = function(event){
$(event.target).css('text-indent','1px');
}
var click2 = function(event){
console.log($(event.target).text());
}
var click3 = function(event){
console.log('clicked');
}
var mouseover1 = function(){
console.log('mouseover');
}
$('div.cards').on('click','div',click1)
.on('click','div',click2)
.on('click','div',click3)
.on('mouseover','div',mouseover1);
$('div.cards').off(); //移除多个事件处理
});
</script>
(三)事件实例对象的属性和方法
1.事件实例对象的属性
属性名 | 说明 | 属性名 | 说明 |
---|---|---|---|
altkey | data | ||
bubbles | detail | ||
button | delegateTarget | ||
cancelable | eventPhase | ||
charCode | metaKey | ||
clientX | namesoace | ||
clientY | offsetX | ||
ctrlKey | offsetY | ||
currentTarget | |||
originalTarget | screenX、screenY | ||
originalEvent | shiftKey | ||
pageX | target | ||
pageY | timeStamp | ||
prevValue | type | ||
relatedTarget | view | ||
result | which |
PS:标红的表示浏览器兼容性有问题,并且JQuery对这个有兼容性问题的属性做了兼容性处理
2.事件实例对象的方法
- preventDefault() 通知浏览器不要执行与事件关联的默认动作(例如:点击按钮表单提交,点击超链接页面跳转等)
- stopPropagation() 阻止当前事件在DOM树上冒泡。
- stopImmediatePropagation() 阻止事件冒泡,并阻止之后绑定于本元素上其他事件的执行。(意思就是只执行第一个绑定的事件)
- isDefaultPrevented()检测是否调用了preventDefault()方法
- isPropagationStopped()检测是否调用了stopPropagation()方法
- isImmediatePropagationStopped()检测是否调用了stopImmediatePropagation()方法
(1)stopPropagation()
<div id="div1">
<div id="div1-1">
<div id="div1-1-1">
<div id="div1-1-1-1">
<div id="p1">
事件冒泡测试
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="//cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script type="text/javascript">
$(function(){
$('div').on('click',function(event){
event.stopPropagation();
console.log('clicked %o',$(this)); //结果只会在控制台中打印出<div id="p1">元素的jQuery对象
});
});
</script>
(2)stopImmediatePropagation()
//使用上面的例子
<script type="text/javascript">
$(function(){
$('div').on('click',function(event){
event.stopPropagation();
console.log('clicked %o',$(this)); //只是阻止了事件冒泡,后面绑定的事件正常执行
}).on('click',function(){
console.log('clicked');
});
});
</script>
//使用stopImmediatePropagation()
<script type="text/javascript">
$(function(){
$('div').on('click',function(event){
event.stopImmediatePropagation();
console.log('clicked %o',$(this));
}).on('click',function(){
console.log('clicked'); //不仅阻止了冒泡,,后面绑定的事件也阻止了
});
});
</script>
(四)触发事件(使用代码触发)
- trigger(eventType[,data])效果与真实触发类似,有冒泡,所有匹配元素都会触发。
- triggerHandler(eventType[,data])
triggerHandler()与trigger()的区别
- 不会触发浏览器默认事件
- 不会产生事件冒泡
- 只会触发jQuery对象集合中第一个元素的事件处理函数
返回事件处理函数的返回值,而不是jQuery对象。
无法直接使用链式语法
(五)快捷方法添加事件
在(一)中我们使用.on(eventType[,seletor][,data][,hander])这个方法添加DOM事件
jQuery还提供了更便捷的添加事件的方法
使用方法
- eventName([data,]handler) 快捷的注册事件的方法
- eventName() 快捷的触发事件的方法
eventName | eventName |
---|---|
blur() | keydown() |
change() | keypress() |
click() | keyup() |
dblclick() | |
error() | |
focus() | |
focusin() | |
focusout() | |
mousedown() | read() |
mouseenter() | resize() |
mouseleave() | scroll() |
mousemove() | select() |
mouseout() | submit() |
mouseover() | unload() |
mouseup() |
hover方法(重要)
hover()是jQuery专门设置的一个方法。
hover()方法相当于同时调用了mouseenter()和mouseleave()方法,可以避免鼠标进入子元素触发父元素mouseout事件的现象
而mouseenter()和mouseleave()弥补了mouseover()和mouseout()的不足。
- hover(enterHandler,leaveHandler)
- hover(handler)
(六)自定义事件
自定义事件不是真正意义上的事件,可以把它理解为自定义函数,触发自定义事件就相当于调用自定义函数。
使用的是on()方法
参数eventType就是自定义的事件名称
通过其他内置事件执行trigger()方法来触发自定义事件
(七)事件命名空间
- eventName.namespace
使用命名空间,可以区分同类型的事件方法,触发指定的事件方法,而不会触发所有同类型的事件方法
<body>
<ul>
<li class="item1">新闻标题-1</li>
<li class="item2">新闻标题-2</li>
<li class="item3">新闻标题-3</li>
<li class="item4">新闻标题-4</li>
<li class="item5">新闻标题-5</li>
<li class="item6">新闻标题-6</li>
<li class="item7">新闻标题-7</li>
<li class="item8">新闻标题-8</li>
<li class="item9">新闻标题-9</li>
</ul>
<button id="even">点击偶数</button>
<button id="odd">点击奇数</button>
<button id="all">全部点击</button>
<script src="../../../vendor/jquery-1.12.4.js"></script>
<script>
$(function() {
$('li:odd').on('click.even', function(e) { //这里就是定义了事件命名空间eventName.namespace,
console.log('%o 偶数', $(this));
});
$('li:even').on('click.odd', function(e) {
console.log('%o 奇数', $(this));
});
$('li').eq(0).on('click.even.0', function(e) {
console.log('%o 0', $(this));
});
$('#even').click(function() {
//使用trigger可以触发on中事件
$('li').trigger('click.even'); //这里使用了事件命名空间,
});
$('#odd').click(function() {
$('li').trigger('click.odd');
});
$('li').off('.even');
});
</script>
</body>
(八)综合案例
使用自定义事件和事件委托实现标签页的切换
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>标签页的切换</title>
<style>
*{margin: 0;padding: 0;}
li{list-style-type: none;}
.main{
width: 70%;
margin: 0 auto;
}
/*标签页*/
.main .tab{
width: 100%;
margin: 100px 0;
}
.tab .tab-nav{
width: 100%;
height: 50px;
}
.tab .tab-nav ul{
height: 48px;
border-bottom: 2px solid #0066ff;
padding: 0 80px;
}
.tab .tab-nav .nav-li{
width: 100px;
height: 46px;
line-height: 46px;
text-align: center;
float: left;
color: #FFFFFF;
border: 2px solid #0066FF; /*li的盒子的高度为54px,底部边框会和.tab-content的顶部边框重合*/
border-radius: 12px 12px 0 0;
background-color: #0066FF;
margin: 0 2px;
cursor: pointer;
}
.tab .tab-nav .active{
border-bottom-color: #FFFFFF;
background-color: #FFFFFF;
color: #000000;
}
.tab .tab-content{
width: auto;
height: 450px;
border:2px solid #0066FF;
border-top:0px;
position: relative;
}
.tab .tab-content .tab-item{
width: 100%;
height: 450px;
position: absolute;
display: none;
}
.tab .tab-content .active{
display: block;
}
/*/标签页*/
</style>
</head>
<body>
<div class="main">
<!-- tab标签页 -->
<div class="tab" id="tab">
<!-- 标签页导航 -->
<div class="tab-nav">
<ul>
<li class="nav-li active">HTML</li>
<li class="nav-li">JS</li>
<li class="nav-li">CSS</li>
<li class="nav-li">jQuery</li>
</ul>
</div>
<!-- /标签页导航 -->
<!-- 标签页内容 -->
<div class="tab-content">
<div class="tab-item active"><h1>HTML</h1></div>
<div class="tab-item"><h1>JS!</h1></div>
<div class="tab-item"><h1>CSS!!</h1></div>
<div class="tab-item"><h1>jQuery!!!</h1></div>
</div>
<!-- /标签页内容 -->
</div>
<!-- /tab标签页 -->
</div>
<script src="//cdn.bootcss.com/jquery/1.12.4/jquery.js"></script>
<script type="text/javascript">
$(function(){
//给标签页绑定事件
$('#tab').on('switchTab','li',function(event,liObj,liIndex){ //自定义事件
//导航切换
liObj.siblings('li').removeClass('active').end().addClass('active');
//内容项切换
$('#tab div.tab-item').removeClass('active').eq(liIndex).addClass('active');
}).click(function(event){
$(event.target).trigger('switchTab',[$(event.target),$(event.target).index()]); //触发自定义事假
});
});
</script>
</body>
</html>