在JavaScript中,事件代理又称为事件委托,就是把子对象的监听事件绑定到事件源对象的父对象上,利用事件冒泡原理触发事件。
优点:
1. 提高性能,不需要所有对象都绑定事件;
2. 灵活,动态创建的新对象不需要绑定事件。
缺点:
1、有的处理不方便而可能影响速度,比如在对象上的鼠标移入和鼠标移出处理;
2、需要有选择地阻止事件冒泡。
1、捕获和冒泡
事件的冒泡:事件从DOM树的底层,层层向外传递,直至传递到DOM的根节点。
事件的捕获:事件从DOM树的根节点,层层向里传递,直至传递到DOM的最底层。
阻止冒泡事件:
W3C标准:event.stopPropagation();IE9以下的低版本不支持
2、事件源对象
用来获取事件的详细信息,如:键盘和鼠标的状态、鼠标点击的对象等。
3、事件监听的挂载
domObj.addEventListener(eventType,func,eventModule)
eventType:事件类型,不加on
func:事件发生后的回调函数
eventModule:boolean值,代表事件模型,false代表冒泡模型,true代码捕获模型,默认是false。
4、举例
在《JavaScript(10):结构、样式、行为的逐步分离》,有表格的举例,还可以改写脚本让代码精炼一些,就是使用了两个循环。
实际结果图:
网页文件内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript事件代理</title>
<link rel="stylesheet" href="JavaScript事件代理.css">
<script src="JavaScript事件代理.js"></script>
</head>
<body onload="init()">
<div id="demo">
<h1 align="center">学生成绩表</h1>
<table align="center" class="table" bgcolor="#cccccc" id="studentTable">
<tr class="rowTitleColorSet">
<td>学号</td>
<td>姓名</td>
<td>语文</td>
<td>数学</td>
<td>平均分</td>
<td>操作</td>
</tr>
</table>
</div>
</body>
</html>
样式文件JavaScript事件代理.css
body{
font-family:Arial,Helvetica,sans-serif;
font:24px "微软雅黑";
}
.table {
cellpadding:1;
cellspacing:1;
}
.table tr{
line-height:200%;
}
.table td{
width: 100px;
}
.rowTitleColorSet{background-color: #818080;color: rgb(232, 232, 232);text-align: center;}
.evenRowColorSet{background-color: #efefef;color: rgb(8, 8, 8);text-align: center}
.oddRowColorSet{background-color: #f8f8f8;color: rgb(128, 128, 128);text-align: center}
.focusRowColorSet{background-color: #999;color:#d70008;text-align: center}
代码文件JavaScript事件代理:
function init(){
//创建studentList对象
var studentList=[
{Id:101,Name:'小明',ChineseScore:81.5,MathScore:87},
{Id:102,Name:'小黄',ChineseScore:61,MathScore:47.5},
{Id:103,Name:'小丽',ChineseScore:89.5,MathScore:83},
{Id:104,Name:'小宋',ChineseScore:56,MathScore:97},
{Id:105,Name:'小王',ChineseScore:82,MathScore:73},
{Id:106,Name:'小李',ChineseScore:31,MathScore:63},
{Id:107,Name:'小华',ChineseScore:49,MathScore:83},
]
var table=document.getElementById("studentTable");
for(i in studentList){
var trTemp=document.createElement("tr");//创建行对象
//设置行样式
if(parseInt(i)%2==0){
trTemp.className="evenRowColorSet"
}else{
trTemp.className="oddRowColorSet"
}
//给行添加事件响应
trTemp.onmouseenter=funcMouseenter;//鼠标移入事件
trTemp.onmouseout=funcMouseout;//鼠标移出事件
//添加td对象
for(item in studentList[i]){
//创建td
var tdTmp=document.createElement("td");
tdTmp.style="width: 100px;";
tdTmp.appendChild(document.createTextNode( studentList[i][item] ));
trTemp.appendChild(tdTmp);
}
//增加平均分
var tdAverageScore=document.createElement("td");
tdAverageScore.style="width: 100px;";
AverageScore=(studentList[i].MathScore+studentList[i].MathScore)/2;
tdAverageScore.appendChild(document.createTextNode( AverageScore ));
trTemp.appendChild(tdAverageScore);
//增加操作栏
var tdbtn=document.createElement("td");
tdbtn.style="width: 100px;";
var btn=document.createElement('input');
btn.type="button";
btn.id=item;
btn.value="删除";
btn.onclick=function(){ del(this); };
//添加到行对象
trTemp.appendChild( btn );
//添加到表格对象
table.appendChild(trTemp);
}
}
function funcMouseenter(event){
this.className='focusRowColorSet'
}
function funcMouseout(event){
var studentID=this.cells[0].innerHTML;
if(parseInt(studentID)%2==0){
this.className="evenRowColorSet"
}else{
this.className="oddRowColorSet"
}
}
function del(obj){
var tr = obj.parentNode;
var studentID=tr.cells[0].innerHTML;
var flag = confirm("确认删除学号为" + studentID + "记录吗?");
if(flag) {//确认删除
//从数据库中删除学号为studentID:delFromDB(studentID)
tr.parentNode.removeChild(tr);//删除tr
}
//return false
}
上面的脚本文件挂载事件过多,因为JavaScript的事件排队机制,还是会影响浏览器的性能,那么这里就可以使用事件代理,将这些事件监听全部委托为表格即可。
使用事件委托改写后的脚本文件JavaScript事件代理.js:
function init(){
//创建studentList对象
var studentList=[
{Id:101,Name:'小明',ChineseScore:81.5,MathScore:87},
{Id:102,Name:'小黄',ChineseScore:61,MathScore:47.5},
{Id:103,Name:'小丽',ChineseScore:89.5,MathScore:83},
{Id:104,Name:'小宋',ChineseScore:56,MathScore:97},
{Id:105,Name:'小王',ChineseScore:82,MathScore:73},
{Id:106,Name:'小李',ChineseScore:31,MathScore:63},
{Id:107,Name:'小华',ChineseScore:49,MathScore:83},
]
var table=document.getElementById("studentTable");
for(i in studentList){
var trTemp=document.createElement("tr");//创建行对象
//设置行样式
if(parseInt(i)%2==0){
trTemp.className="evenRowColorSet"
}else{
trTemp.className="oddRowColorSet"
}
//添加td对象
for(item in studentList[i]){
//创建td
var tdTmp=document.createElement("td");
tdTmp.style="width: 100px;";
tdTmp.appendChild(document.createTextNode( studentList[i][item] ));
trTemp.appendChild(tdTmp);
}
//增加平均分
var tdAverageScore=document.createElement("td");
tdAverageScore.style="width: 100px;";
AverageScore=(studentList[i].MathScore+studentList[i].MathScore)/2;
tdAverageScore.appendChild(document.createTextNode( AverageScore ));
trTemp.appendChild(tdAverageScore);
//增加操作栏
var tdbtn=document.createElement("td");
tdbtn.style="width: 100px;";
var btn=document.createElement('input');
btn.type="button";
btn.id=item;
btn.value="删除";
//添加到行对象
trTemp.appendChild( btn );
//添加到表格对象
table.appendChild(trTemp);
}
var oldSelectRow;
var currentSelectRow;
table.addEventListener('click',function(e){
if(e.target.type=='button'){
var tr = e.target.parentNode;
var studentID=tr.cells[0].innerHTML;
var flag = confirm("确认删除学号为" + studentID + "记录吗?");
if(flag) {//确认删除
//从数据库中删除学号为studentID:delFromDB(studentID)
tr.parentNode.removeChild(tr);//删除tr
}
}
})
table.addEventListener('mouseover',function(e){//鼠标在表格内移动
if(e.target.parentElement.rowIndex){
//在表格的行上移动
currentSelectRow=e.target.parentElement;//得到当前的行对象并记录
e.target.parentElement.className="focusRowColorSet";//将当前行置为焦点行
if(currentSelectRow!=oldSelectRow){
if(oldSelectRow){
trIndex=oldSelectRow.rowIndex;//得到当前行对象的所在行号
if(parseInt(trIndex)%2==0){
oldSelectRow.className="evenRowColorSet";
}else{
oldSelectRow.className="oddRowColorSet";
}
}
oldSelectRow=currentSelectRow;
}
}
})
table.addEventListener('mouseout',function(e){//鼠标移出表格事件
if(currentSelectRow){
var trIndex=currentSelectRow.rowIndex;//得到当前行对象的所在行号
if(parseInt(trIndex)%2==0){
currentSelectRow.className="evenRowColorSet";
}else{
currentSelectRow.className="oddRowColorSet";
}
}
oldSelectItem=null;
currentSelectItem=null;
})
}
上面代码的改写完成了同样的功能,就是使用了事件委托的原理,这样优点很明显也有弊端,可能造成卡顿现象,脚本也不容易调试。这么简单的改写,我费了半个多小时。