JS 事件分层及性能优化
HTML 事件处理程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
<script>
function clickHander() {
alert('click');
}
</script>
</head>
<body>
<p onclick="alert(event.type)">hi jiavan</p>
<p onclick="clickHander()">handler</p>
</body>
</html>
html 事件处理程序的主要缺点 0. 可能会出现事件被触发了而事件处理程序还没有被加载
- 文档行为与文档结构耦合程度高
DOM 0 级事件处理程序
将函数值赋值给一个事件处理程序属性。每个元素都有自己的事件处理程序属性:
var bt = document.getElementById('bt');
bt.onclick = function () {
alert('hi');
};
bt.onclick = null; //删除事件处理程序
DOM0 级事件处理程序被认为是元素的方法,因此事件处理程序在元素的作用域下运行,this 指向的是当前的元素。
DOM 2 级事件处理程序
var btHandler = function () {
alert(this.id);
};
var btHandler2 = function () {
alert(this.title);
};
var bt = document.getElementById('bt');
bt.addEventListener('click', btHandler, false);
bt.addEventListener('click', btHandler2, false);
bt.removeEventListener('click', btHandler2, false); //移除事件
优点 0. 能添加多个事件处理程序,按添加顺序调用
- 方便移除事件处理程序,能设置在什么阶段触发
- 行为与结构分离
跨浏览器的事件处理
var EventUtil = {
addHandler: function (element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false); //DOM2级事件处理
} else if (element.attachEvent) {
element.attachEvent('on' + type, handler); //IE事件处理
} else {
element['on' + type] = handler; //DOM0级事件处理
}
},
removeHandler: function (element, type, handler) {
if (element.removeHandler) {
element.removeEventListener(type, handler, false);
} else if (element.attachEvent) {
element.detachEvent('on' + type, handler);
} else {
element['on' + type] = null;
}
}
};
var p = document.getElementById('jiavan');
EventUtil.addHandler(p, 'click', function () {
alert(this.id);
});
EventUtil.addHandler(p, 'mouseover', function() {
alert('mouseover');
});
事件对象
在触发 DOM 上的某个事件时,会产生一个 event 对象,该对象包含了与事件有关的信息。在事件处理程序内部,this 始终等于 currentTarget,而 target 只包含事件实际目标,如果事件处理程序直接给了目标元素,那么 3 个值相等。
var btn = document.getElementById('btn');
document.body.onclick = function (event) {
alert('body'); //事件在传播到body之前被阻止,所以此处不会执行
};
btn.addEventListener(
'click',
function (event) {
alert('click');
event.stopPropagation(); //立即停止事件在DOM层次中传播,可取消事件冒泡或事件捕获
},
false
);
注:event 只会存在事件处理程序执行期间,一旦处理完成,该对象将会被销毁。
UI 事件
load
当页面完全加载后就会触发 window 上面的 load 事件,图像上面也可以触发 load 事件。
window.addEventListener(
'load',
function () {
alert('window load');
},
false
);
var image = document.createElement('img');
image.addEventListener(
'load',
function () {
alert('img load');
},
false
);
image.src = 'https://avatars3.githubusercontent.com/u/6786013?v=3&s=460';
注意:当 img 设置完 src 属性后就会下载图片,所以应该在 src 设置之前绑定事件处理程序。
unload,resize,scroll 事件
window.addEventListener(
'unload',
function () {
console.log('unload');
},
false
);
window.addEventListener('resize', function () {
console.log('resize');
});
window.addEventListener(
'scroll',
function () {
console.log('scroll');
},
false
);
鼠标事件
通过 event 对象获得鼠标事件产生的位置信息。
event.clientX/Y,在浏览器窗口中发生的位置
event.pageX/Y,在页面中发生的位置
所以在没有滚动的页面下,clientX/Y 的值和 pageX/Y 的值是相等的
如果不支持事件对象的页面,可以通过 document.body(混杂模式),document.documentElement(标准模式)中的 scrollTop 和 scrollLeft 属性计算。
事件位置信息还可以是相对于整个屏幕的位置,可以用 screenX/Y 属性来获得。
HTML5 事件
beforeunload
在浏览器页面卸载之前触发,可以取消卸载并继续使用原来的页面,为了显示弹出对话框,必须将 event.returnValue 的值设置为要显示给用户的字符串,同时作为函数的值返回。
window.addEventListener(
'beforeunload',
function (event) {
var message = '真的要离开吗?';
event.returnValue = message;
return message;
},
false
);
注:事件发生在 window 上,并且要将 returnValue 和函数返回值设置为要显示的信息。
事件性能和内存
添加到页面中的事件处理程序数量将会直接影响到页面整体运行性能,导致这一问题的原因是多方面的,首先每个函数都是对象,都会占用内存,其次频繁访问 DOM 会延迟整个页面的交互就绪时间。
事件处理程序过多问题的解决方案是事件委托,事件委托利用了事件冒泡,指定一个事件处理程序,就可以管理某一类型的所有事件,例如 click 事件会一直冒泡到 document 层次,也就是说,我们可以为整个页面指定一个 onclick 事件处理程序,而不必给每个元素添加事件处理程序。
如果可行,可以给 document 对象添加一个事件处理程序,处理页面上发生的某种特定类型事件,与传统方法相比,具有如下优点: 0. document 对象很快就可以访问
- DOM 引用更少
- 整个页面占用的内存空间更少,能提升整体性能
document.addEventListener(
'click',
function (event) {
var target = event.target;
switch (target.id) {
case 'one':
alert('one');
break;
case 'two':
alert('two');
break;
case 'three':
alert('three');
break;
}
},
false
);