事件的传播机制,捕获和冒泡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.fa {
width: 300px;
height: 300px;
background-color: pink;
}
.fa .son {
width: 150px;
height: 150px;
background-color: blue;
}
</style>
</head>
<body>
<div class="fa">
<div class="son"></div>
</div>
<script type="text/javascript">
/*
DOM0级事件绑定的方法都是在当前元素事件行为的冒泡阶段执行
*/
var fa = document.querySelector(".fa");
var son = document.querySelector(".son");
var result = null;
// window.onclick = function() {
// console.log("window");
// }
// document.onclick = function() {
// console.log("document");
// }
// document.documentElement.onclick = function() {
// console.log("html");
// }
// document.body.onclick = function() {
// console.log("body");
// }
// fa.onclick = function() {
// console.log("fa");
// }
// son.onclick = function() {
// console.log("son");
// }
// DOM2级绑定事件
// addEventListener(事件名称,事件回调函数,是否捕获)
// 第三个参数 true表示捕获阶段绑定 false表示冒泡阶段 默认值也是false
window.addEventListener("click", function(e) {
console.log("window 捕获");
console.log(e.target);
console.log(result);//null
}, true);
document.addEventListener("click", function(e) {
console.log("document 捕获");
console.log(result == e)//false 因为是true,捕获,由外向里,此时result仍然为null
}, true);
document.body.addEventListener("click", function() {
console.log("body 捕获");
}, true);
fa.addEventListener("click", function() {
console.log("fa 捕获");
},true);
son.addEventListener("click", function(e) {
console.log("son 捕获");
result = e;
console.log(result == e)//true
},true);
// window.addEventListener("click", function(e) {
// console.log("window 捕获");
// console.log(e.target);
// });
// document.addEventListener("click", function() {
// console.log("document 捕获");
// console.log(result == e)//true
// });
// document.body.addEventListener("click", function() {
// console.log("body 捕获");
// });
// fa.addEventListener("click", function() {
// console.log("fa 捕获");
// });
// son.addEventListener('click',function(){
// console.log('son冒泡');
// resule = e;
// })
//事件捕获和事件冒泡的区别
// 他们是描述事件触发时序问题的术语。事件捕获指的是从document到触发事件的那个节点,即自上而下的去触发事件。相反的,事件冒泡是自下而上的去触发事件。绑定事件方法的第三个参数,就是控制事件触发顺序是否为事件捕获。true,事件捕获;false,事件冒泡。默认false,即事件冒泡。
</script>
</body>
</html>
事件委托的含义,使用,优点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style type="text/css">
ul,ol{
list-style:none;
}
</style>
<body>
<ul id="oul">
<li>1111</li>
<li>2222</li>
<li>3333</li>
<li>4444</li>
<li>5555</li>
</ul>
<ul id="test">
<li>
<p>11111111111</p>
</li>
<li>
<div>
22222222
</div>
</li>
<li>
<span>3333333333</span>
</li>
<li>4444444</li>
</ul>
<button id="btn">点击插入新的li</button>
</body>
<script type="text/javascript">
function $(id){
return document.getElementById(id);
}
//为ul下的每个li添加点击事件,点击li打印对应的li的文本
//传统方法 遍历循环绑定
// var olis = document.querySelectorAll("li");
// for(var i = 0;i<olis.length;i++){
// olis[i].onclick = function(){
// console.log(this.innerText);
// }
// }
//采用事件委托的方法
$('oul').onclick = function(e){
var e = e||event;//兼容ie的写法
var target = e.target||e.srcElement;//兼容ie的事件对象target的属性的写法
console.log(target.innerText);
}
//为ul动态添加元素li
var count = 0;
$('btn').onclick = function(){
count++;
var oli = document.createElement('li');
oli.innerText = '我是新插入进来的第'+count+'个'+'li';
$('oul').appendChild(oli);
}
//什么叫事件委托?
//事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件
//事件委托的原理
// 事件委托是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开始,然后逐步向上传播事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比如给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a>li>ul>div,有这样一个机制,那么我们给最外面的div加点击事件,那么里面的ul,li,a做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件委托,委托它们父级代为执行事件。
//为何用事件委托,有何优点?
// 一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?
// 在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;
// 每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了(内存不够用,是硬伤,哈哈),比如上面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能说呵呵了,如果用事件委托,那么我们就可以只对它的父级(如果只有一个父级)这一个对象进行操作,这样我们就需要一个内存空间就够了,是不是省了很多,自然性能就会更好。
// Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,用nodeName来获取具体是什么标签名
//用事件委托的方式,新添加的子元素是带有事件效果的,我们可以发现,当用事件委托的时候,根本就不需要去遍历元素的子节点,只需要给父级元素添加事件就好了,其他的都是在js里面的执行,这样可以大大的减少dom操作,这才是事件委托的精髓所在。
// 关于事件委托的另外一个实例
// var oUl = document.getElementById('test');
// oUl.addEventListener('click',function(ev){
// var target = ev.target;
// while(target !== oUl ){
// if(target.tagName.toLowerCase() == 'li'){
// console.log('li click~');
// break;
// }
// target = target.parentNode;
// }
// })
$('test').onclick =function(e){
var e = e||event;
var target = e.target||e.srcElement;
while(target!=$('test')){
if(target.nodeName.toLowerCase() == 'li'){
console.log('可以');
break;
}
target = target.parentNode;
}
}
// 核心代码是while循环部分,实际上就是一个递归调用,也可以写成一个函数,用递归的方法来调用,同时用到冒泡的原理,从里往外冒泡,知道currentTarget为止,当当前的target是li的时候,就可以执行对应的事件了,然后终止循环,恩,没毛病!
// 适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。
// 值得注意的是,mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。
// 不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。
</script>
</html>
一个打字游戏的综合练习
//需要引入一个第三方的css动画库
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>打字练习</title>
<link rel="stylesheet" type="text/css" href="animate.css">
<style type="text/css">
body{
margin:0;
display: flex;
justify-content: center;
align-items: center;
/*设置背景颜色的经像渐变*/
background: radial-gradient(circle,#444,#111,#000);
text-align: center;
}
#char{
font-size: 400px;
color: lightgreen;
/*设置文字阴影*/
/*text-shadow: 水平位置 垂直位置 模糊距离 阴影颜色*/
/*位置可以为负值*/
text-shadow: 0 0 50px #666;
}
#result{
font-size: 20px;
color:#888;
}
#char.error{
color:red;
}
</style>
</head>
<body>
<div>
<div id="char">A</div>
<div id="result">请在键盘上按下屏幕上显示的字母</div>
</div>
</body>
<script type="text/javascript">
//让屏幕随机出现26个大写字母 A-Z 65-90
//封装一个产生介于某两个数之间随机数的函数 需要两个参数,最大值和最小值
function getRandomCount(min,max){
if(min>max){
var temp = min;
min = max;
max = temp;
}
return parseInt(Math.random()*(max-min+1)+min);
//0<=Math.random()<1;
//0<=Math.random()*(90-65)<25
//0<=Math.random()*25<25
//0<=Math.random()*26<26
//0+65<=Math.random()*26+65<26+65
//65<=Math.random()*(90-65+1)+65<91
}
// console.log(getRandomCount(10,20));
function $(id){
return document.getElementById(id);
}
//根据得到的随机数去随机的得到一个字符
var code;
function getString(){
code = getRandomCount(65,90);//调用产生随机数的方法
var oneString = String.fromCharCode(code);
$('char').innerText = oneString;
}
getString();//初始化时先赋值一个
//创建键盘键盘按下的事件函数
var countright = 0,countwrong = 0;
document.onkeydown = function(e){
var e = e||event;
if(e.keyCode == code){
//键盘按下后如果是按下的显示出来的字母添加一个代表正确的动画效果
$('char').className = 'animated zoomIn';
countright++;
}else{
//键盘按下后如果不是按下的显示出来的字母添加一个代表错误的动画效果
$('char').className = 'animated shake error';
countwrong++;
}
//无论是否按对都将添加的效果去除
setTimeout(function(){
$('char').className = '';
getString();//重新初始化显示一个字母
},200);
//调用统计正确率的
getRingtData();
}
//统计正确率
function getRingtData(){
var res = (countright/(countright+countwrong)*100).toFixed(2)+'%';
$('result').innerText ='正确率为'+res;
}
</script>
</html>
网友评论