浏览器的事件模型
在js中有两种事件模型:
DOM 0级事件模型 基本所有浏览器都支持
DOM 2级事件模型 绝大部分现代浏览器支持
还是上代码举例说明8
<p id="example" onclick="console.log('Click');">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officiis praesentium, sapiente? Earum, molestiae rem. Enim, perspiciatis quisquam! A consequatur culpa error et, natus nobis placeat qui rem, suscipit tempora tenetur.
</p>
<script>
document.getElementById('example').onclick = function(event) {
console.log('click me');
};
document.getElementById('example').onclick = function(event) {
event = event || window.event;//浏览器兼容问题
console.log('click me2');
};
</script>
事件只能绑定一次,后面绑定的会覆盖前面的。
除此之外,事件执行时还会检测父元素有没有事件,要是有,依次触发,例如:
<div id="p1">
<div id="p1-1">
<div id="p1-1-1">
<div id="p1-1-1-1">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet architecto beatae delectus enim ipsum iusto laborum, libero minus perspiciatis quae quidem rem sed soluta velit veniam voluptatem voluptates voluptatibus. Repudiandae.
</p>
</div>
</div>
</div>
</div>
document.getElementById('p1').onclick = function() {
console.log('p1');
};
document.getElementById('p1-1').onclick = function() {
console.log('p1-1');
};
document.getElementById('p1-1-1').onclick = function() {
console.log('p1-1-1');
};
document.getElementById('p1-1-1-1').onclick = function(e) {
console.log('p1-1-1-1');
// e.cancelBubble = true;
// e.stopPropagation();
};
这就是事件冒泡。当然如果我加上上面任意一行代码就可以取消冒泡了
最大的缺点就是只支持一个DOM事件处理函数,所以有了DOM2级事件模型。
attachEvent()是针对IE浏览器支持的
var element = document.getElementById('example');
element.addEventListener('click', function(event) {
console.log('Click1');
}, false);
element.addEventListener('click', function(event) {
console.log('Click2');
}, false);
element.addEventListener('click', function(event) {
console.log('Click3');
}, false);
此时点击一次,多个事件都会依次执行,不会覆盖。
DOM 2级事件除了有冒泡机制还加入了捕获机制,上面设置为false就是指只冒泡不捕获,如果为true则相反。
var useCapture = true;
document.getElementById('p1').addEventListener('click', function() {
console.log('p1');
}, useCapture);
document.getElementById('p1-1').addEventListener('click', function() {
console.log('p1-1');
}, useCapture);
document.getElementById('p1-1-1').addEventListener('click', function() {
console.log('p1-1-1');
}, useCapture);
document.getElementById('p1-1-1-1').addEventListener('click', function() {
console.log('p1-1-1-1');
}, useCapture);
此时就是捕获,从上往下执行。(为了兼容性,很少使用,基本用冒泡)
那如何实现既捕获又冒泡呢?
其实就是执行两遍就行,先捕获,然后冒泡。
为了处理兼容性问题,对于事件的处理,通常会使用下面的代码:
var EventsUtility = {
addEvent: function (element, type, callback) {
if (typeof addEventListener !== 'undefined') {
element.addEventListener(type, callback, false);
} else if (typeof attachEvent !== 'undefined') {
element.attachEvent('on' + type, callback); // IE, legacy browser
} else {
element['on' + type] = callback; //DOM 0级
}
},
removeEvent: function (element, type, callback) {
if (typeof removeEventListener !== 'undefined') {
element.removeEventListener(type, callback, false);
} else if (typeof detachEvent !== 'undefined') {
element.detachEvent('on' + type, callback); // IE, legacy browser
} else {
element['on' + type] = null;
}
},
getTarget: function (event) {
if (typeof event.target !== 'undefined') {
return event.target;
} else {
return event.srcElement; // IE, legacy browser
}
},
preventDefault: function (event) {
if (typeof event.preventDefault !== 'undefined') {
event.preventDefault();
} else {
event.returnValue = false;
}
},
getCharCode: function (event) {
if (typeof event.charCode === 'number') {
return event.charCode;
} else {
return event.keyCode; // IE, legacy browser
}
}
};
接下来进入主题:jQuery事件
<div class="item">
<p>Lorem ipsum dolor sit amet.</p>
</div>
<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>
<div id="p1">
<div id="p1-1">
<div id="p1-1-1">
<div id="p1-1-1-1">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet architecto beatae delectus enim ipsum iusto laborum, libero minus perspiciatis quae quidem rem sed soluta velit veniam voluptatem voluptates voluptatibus. Repudiandae.
</p>
</div>
</div>
</div>
</div>
$(function() {
$('p')
.on('click', null, null, function (e) {
console.log('p clicked');
})//这里获取的是p的一个集合,不管点哪个p都会触发
.on('click', function (e) {
console.log('p clicked2');
})//不加null也一样
.on('click',function (e) {
console.log('p clicked3';
})//触发链式
.on('click', { foo: 'bar' }, function (e) {
console.log('p clicked3 %o', e.data);
})//给它加数据,%o为占位符
.on('mouseover', function (e) {
console.log('p mouseover');
})
.on('mouseover', function (e) {
console.log('p mouseover2');
})
$('div').on('click','#p1-1', function () {
console.log(pp);
})//过滤,只选择id为对应的才触发
//事件委托
$('ul').on('click', 'li.item2', function () {
console.log('li clicked');
})
$('li').on('click', function (e) {
console.log('Clicked: %o', $(this));
})
//给父节点添加,根据target不同,效果一样
$('ul').on('click', 'li', function (e) {
if(event.target)//如果是li元素就执行
console.log('Clicked: %o', $(e.target));
})//根据冒泡,点击li,会冒泡到ul,然后触发,这样建少了绑定事件数目
//这就是事件的委托,委托在父元素上,提升性能
})
$('p').on('click', function (e) {
e.stopPropagation()
console.log('clicked p');
})
一次性的事件处理:
one(eventType[, selector][, data],handler)
执行完一次就自动销毁了
$('p').one('click', function(e) {
console.log('clicked p');
});
移除事件
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur assumenda debitis fugiat laboriosam magni modi sequi, sint ullam! Ad consequatur cum excepturi modi nam quos. Dolor est laudantium minima ratione.</p>
var clicked2 = function(e) {
console.log('clicked2');
};
$('p').on('click', function(e) {
console.log('clicked');
}).on('click', clicked2).on('mouseover', function(e) {
console.log('mouseover');
});
$('p').off('click')//如果换成下面的,则会移除指定的
$('p').off('click', clicked2)
});
off事件不管你绑定了多少click事件,都会移除所有的click事件。
当然也可以移除指定的事件。
如果想移除多个不同事件,可以用下面的方法,加空格或者啥也不传
$('p').off('click mouseover')
$('p').off()
事件实例对象
这些红色的属性是有浏览器兼容问题的,而且jQuery对这些属性做了兼容性处理。
stopPropagation()只阻止了本身及后续事件的冒泡,而stopImmediatePropagation()除了阻止冒泡以外,还阻止了后续的事件执行。
后3个就是检测前面的方法有没有调用,调用了返回true。
$(function() {
$('div').on('click', function(e) {
console.log('clicked: %o', $(this));
});
$('p').on('click', function(e) {
e.stopPropagation();
console.log('clicked1: p');
console.log(e.isPropagationStopped());
});
$('p').on('click', function(e) {
console.log('clicked2: p');
});
});
触发事件
trigger(eventType[, data])
triggerHandler(eventType[, data])
<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="all">全部标记为已读</button>
<script src="../../../vendor/jquery-1.12.4.js"></script>
<script>
$(function() {
$('li').on('click', function(e, arg1, arg2) {
console.log('%o 已读', $(this));
console.log(arg1);
console.log(arg2);
return $(this);
});
$('#all').on('click', function() {
console.log($('li').trigger('click', [1, 2]));//如果没有后面的数组,则都会触发,加上数组[1,2],其实这是传入的数据。
console.log($('li').triggerHandler('click'));//只会触发第一个
});
});
</script>
</body>
上面的就修改为:
$(function() {
$('li').click(function(e) {
console.log('%o 已读', $(this));
});
$('#all').on('click', function() {
console.log($('li').click());
});
});
hover方法
弥补onmouseover()和onmouseout()的不足
例如下面的代码,当移入outer1时触发mouseover,但当移入outer1里面的inner1时却触发了outer1的mouseout,然后触发inner1的mouseover。这就不符合我们的认识了。
<head>
<meta charset="UTF-8">
<title>demo</title>
<style>
.outer {
width: 200px;
height: 200px;
padding: 20px;
color: #fff;
background-color: green;
}
.inner {
width: 100px;
height: 100px;
margin: 30px auto;
padding: 20px;
color: #fff;
background-color: orange;
}
#outer2 {
margin-top: 50px;
}
</style>
</head>
<body>
<div class="outer" id="outer1">
Outer 1
<div class="inner" id="inner1">Inner 1</div>
</div>
<div class="outer" id="outer2">
Outer 2
<div class="inner" id="inner2">Inner 2</div>
</div>
<script src="../../../vendor/jquery-1.12.4.js"></script>
<script>
$(function() {
function report(event) {
event.stopPropagation();
console.log(event.type + ' on ' + event.target.id);
}
$('#outer1').on('mouseover mouseout', report);
$('#inner1').on('mouseover mouseout', report);
//下面的就不会触发了
$('#outer2').hover(report);
$('#inner2').hover(report);
});
</script>
</body>
jQuery就是提供这两种来解决:
hover(enterHandler,leaveHandler)
hover(handler)
自定义事件
监听器模式和观察者模式
$(function() {
$('li').on('markAsRead', function(e) {
console.log('%o 已读', $(this));
});
$('li').on('click', function(e) {
$(this).trigger('markAsRead');
});
$('#all').on('click', function() {
$('li').trigger('markAsRead');
});
});
事件命名空间
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) {
console.log('%o 偶数', $(this));
});//奇数为什么写偶数呢,因为从0开始计数
$('li').eq(0).on('click.even.0', function(e) {
console.log('%o 0', $(this));
});
$('li:even').on('click.odd', function(e) {
console.log('%o 奇数', $(this));
});
$('#even').on('click', function() {
$('li').trigger('click.even');
});
$('#odd').on('click', function() {
$('li').trigger('click.odd');
});
$('#all').on('click', function() {
// $('li').trigger('click');
$('li').trigger('click.even.0');
});
// $('li').off('.even')
});
</script>
</body>
命名空间作用:除了触发even,还会触发带有even的子的命名空间触发
//去除even事件:
$('li').off('.even')
//去除所有事件:
$('li').off()
一词:puppet 傀儡,木偶
NEXT:
jQuery插件的使用和编写,交互效果来吧!