1.事件
JavaScript和HTML的交互是通过事件实现的。JavaScript采用异步事件驱动编程模型,当文档、浏览器、元素或与之相关对象发生特定事情时,浏览器会产生事件。如果JavaScript关注特定类型事件,那么它可以注册当这类事件发生时要调用的句柄:
如事件是某个行为或者触发,比如点击、鼠标移动;当用户点击鼠标时;当网页已加载时
2 DOM2事件传播机制
当事件发生在某个文档节点上时(即事件目标),目标的事件处理程序就会被触发。此外目标的每个祖先节点也有机会处理该事件。
2级DOM的事件传播包含三个阶段:
1.事件捕捉阶段(capturing),事件具体的节点更早接收事件,而最具体的元素最后接收事件;从顶级文档树节点一级一级向下遍历,直到到达该事件的目标节点。
2.到达事件的目标节点,执行目标节点的时间处理程序。
3.事件冒泡(bubbling),事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的元素;从目标节点一级一级向上上溯,直到顶级文档树节点
<!DOCTYPE html >
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>Test Page</title>
</head>
<body>
<div>
Click Here</div>
</body>
</html>
事件冒泡模型
事件捕获模型
DOM事件流
<style>
.container,
.box,
.target{
border: 1px solid;
padding: 10px;
}
</style>
<button id="btn">click</button>
<div class="container">
container
<div class="box">
box
<div class="target">target</div>
</div>
</div>
<script>
function $(selector){
return document.querySelector(selector)
}
var btn = $('#btn')
btn.onclick = function (e){
console.log(e)
}
btn.addEventListener('click', function(evt){
console.log(this)
console.log(btn)
console.log(evt.target)
})
$('.container').addEventListener('click', function(e){
console.log('contianer click.. in 捕获阶段')
}, true)
$('.box').addEventListener('click', function(e){
console.log('box click.. in 捕获阶段')
}, true)
$('.target').addEventListener('click', function(e){
console.log('target click.. in 捕获阶段')
}, true)
$('.container').addEventListener('click', function(e){
console.log('contianer click.. in 冒泡阶段')
}, false)
$('.box').addEventListener('click', function(e){
console.log('box click.. in 冒泡阶段')
}, false)
$('.target').addEventListener('click', function(e){
console.log('target click.. in 冒泡阶段')
}, false)
</script>
2.1DOM2事件处理程序
DOM2级事件定义了两个方法用于处理指定和删除事件处理程序的操作:
1.addEventListener
2.removeEventListener
所有的DOM节点都包含这两个方法,并且它们都接受三个参数:
事件类型
事件处理方法
布尔参数,如果是true表示在捕获阶段调用事件处理程序,如果是false,则是在事件冒泡阶段处理
var ul=document.querySelector('.ct');
ul.addEventListener('click',function(e){
console.log(e.target.innerText);
});
2.3 阻止默认事件:
preventDafault方法取消浏览器对当前事件的默认行为,比如点击链接后,浏览器跳转到指定页面,或者按一下空格键,页面向下滚动一段距离。该方法生效的前提是,事件的cancelable属性为true如果为fales,则调用该方法没有任何效果。
比如我们可以阻止链接导航这一默认行为
<a href="http://baid.com">baidu</a>
<script>
document.querySelector('a').onclick= function(e){
e.preventDefault()
console.log(this.href)
if(/baidu.com/.test(this.href)){
location.href = this.href
}
}
</script>
2.4 阻止传播
stopPropagation方法阻止事件在DOM中继续传播,即取消进一步的事件捕获或冒泡,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上新定义的事件监听函数。
我们可以在button的事件处理程序中调用stopPropagation()从而避免注册在body上的事件发生
<style>
.container,
.box,
.target{
border: 1px solid;
padding: 10px;
}
</style>
<button id="btn">click</button>
<div class="container">
container
<div class="box">
box
<div class="target">target</div>
</div>
</div>
<script>
function $(selector){
return document.querySelector(selector)
}
var btn = $('#btn')
btn.onclick = function (e){
console.log(e)
}
btn.addEventListener('click', function(evt){
console.log(this)
console.log(btn)
console.log(evt.target)
})
$('.container').addEventListener('click', function(e){
console.log('contianer click.. in 捕获阶段')
}, true)
$('.box').addEventListener('click', function(e){
console.log('box click.. in 捕获阶段')
}, true)
$('.target').addEventListener('click', function(e){
console.log('target click.. in 捕获阶段')
}, true)
$('.container').addEventListener('click', function(e){
console.log('contianer click.. in 冒泡阶段')
}, false)
$('.box').addEventListener('click', function(e){
e.stopPropagation();
console.log('box click.. in 冒泡阶段')
}, false)
$('.target').addEventListener('click', function(e){
console.log('target click.. in 冒泡阶段')
}, false)
</script>
范例:
1.点击按钮会弹出面板
2.点击面板列表会在标题展示点击的内容
3.点击面板以外的区域面板会消失
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<style>
section {
width: 800px;
margin: 0 auto;
position: relative;
}
ul,li {
margin: 0;
padding: 0;
list-style: none;
}
.card {
position: absolute;
border: 1px solid #ccc;
width: 200px;
display: none;
}
.card li {
border-bottom: 1px solid #ccc;
padding: 10px;
cursor: pointer
}
</style>
</head>
<body>
<section>
<h1>你选择了 <span class="choice"></span></h1>
<button class="btn">点我</button>
<ul class="card">
<li>菜单1</li>
<li>菜单2</li>
<li>菜单3</li>
</ul>
</section>
<script>
let choice = document.querySelector('.choice')
let btn = document.querySelector('.btn')
let card = document.querySelector('.card')
let items = document.querySelectorAll('.card li')
btn.onclick = function(e) {
card.style.display = "block"
e.stopPropagation()
}
items.forEach(item => {
item.onclick = function(e) {
choice.innerText = this.innerText
e.stopPropagation()
}
})
document.onclick = function() {
card.style.display = "none"
}
</script>
</body>
</html>
效果:http://js.jirengu.com/jebew
2.5 事件代理
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理
定义:事件代理就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。(delegation)。
var ul = document.querySelector('ul');
ul.addEventListener('click', function(event){
if(event.target.tagName.toLowerCase() === 'li'){
//...
}
})
范例1:
当点击li的时候会在console.log打印输出
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul class="ct">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul=document.querySelector('.ct');
ul.addEventListener('click',function(e){
console.log(e.target.innerText);//"1"
console.log(this.innerText) //" 1 2 3"
});
</script>
</body>
</html>
范例2:
1.当勾选 checkbox 时, 标题的我要学后要展示对应勾选的内容
2.当新增标签后,可勾选新增的标签,切勾选后仍展示到标题上
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<h1>我要学<span class="txt">____</span></h1>
<ul class="box">
<li>前端 <input type="checkbox" value="前端"></li>
<li>Java <input type="checkbox" value="Java"></li>
<li>PHP <input type="checkbox" value="PHP"></li>
</ul>
<div>
<input type="text" class="ipt"> <button class="add"> 新增</button>
</div>
<script>
let checkboxs = document.querySelectorAll('.box [type=checkbox]')
let addBtn = document.querySelector('.add')
let text = document.querySelector('.txt')
let input = document.querySelector('.ipt')
let box = document.querySelector('.box')
let values = []
addBtn.onclick = function() {
let node = document.createElement('li')
node.innerText = input.value
let checkbox = document.createElement('input')
checkbox.type = "checkbox"
checkbox.value = input.value
box.appendChild(node)
node.appendChild(checkbox)
}
box.onclick = function(e) {
console.log(e.target.value)
text.innerText = e.target.value
//添加所有选中元素
// text.innerText = [...document.querySelectorAll('.box [type=checkbox]')].filter(checkbox=>checkbox.checked)
// .map(checkbox=>checkbox.value).join(',')
}
</script>
</body>
</html>
效果:http://js.jirengu.com/baqul
注意:使用事件代理(委托),之后,要有效的操作实际触发的dom节点,可使用.target方法,而不能用this,在这里this指绑定事件的元素,而不是实际触发的元素;
作用:以后再添加子节点,监听函数依然有效
3.1 自定义事件
var EventCenter = {
on: function(type, handler){
document.addEventListener(type, handler)
},
fire: function(type, data){
return document.dispatchEvent(new CustomEvent(type, {
detail: data
}))
}
}
EventCenter.on('hello', function(e){
console.log(e.detail)
})
EventCenter.fire('hello', '你好')
3.2写一个通用的事件侦听器函数
// event(事件)工具集,来源:github.com/markyun
markyun.Event = {
// 视能力分别使用dom0||dom2||IE方式 来绑定事件
// 参数: 操作的元素,事件名称 ,事件处理程序
addEvent : function(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;
}
},
// 移除事件
removeEvent : function(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.datachEvent) {
element.detachEvent('on' + type, handler);
} else {
element['on' + type] = null;
}
},
// 阻止事件 (主要是事件冒泡,因为IE不支持事件捕获)
stopPropagation : function(ev) {
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true;
}
},
// 取消事件的默认行为
preventDefault : function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
// 获取事件目标
getTarget : function(event) {
return event.target || event.srcElement;
}