首先应树立一种HTML、CSS、JavaScript三者独立的观念
JavaScript脚本的事件处理
不要在HTML元素中以属性的方式添加事件处理
<button onclick="doSomething()">Press me</button>
甚至是直接写入脚本代码
<button onclick="alert('Hello, this is my old-fashioned event handler!');">Press me</button>
正确的姿势
①JavaScript中使用事件处理器属性
var btn = document.querySelector('button');
function f() {}
btn.onclick = f;
甚至是批量添加事件处理
var buttons = document.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++) {
buttons[i].onclick = f;
}
②addEventListener()
和removeEventListener()
一看就是更为规范的监听器方法
function f(){}
btn.addEventListener('click', f);
btn.addEventListener('click', f);
使用此方法可为一个对象添加多个事件处理
而使用 element.click=f; 方式添加多个事件处理时,后添加的会覆盖先前的
事件对象
事件对象,在事件触发时被自动传递给事件处理函数,以提供额外的功能和信息
function f(e) {
console.log(e);
}
btn.addEventListener('click', f);
如 e.target 始终是触发事件的对象的引用,(类似直接使用 this,同Java中的this
大多数事件处理器的事件对象都有可用的标准属性和函数
一些特殊的对象会添加一些专业属性提供额外的数据
事件冒泡及捕获
与Android中的事件分发(事件的传递)有相似之处,也就存在类似的问题
事件冒泡和捕获是事件传递的两种方式
在现代浏览器中,默认情况下,所有事件处理程序都在冒泡阶段进行注册,即是说,事件传播默认采用冒泡方式
使用addEventListener()
并将第三个参数置为true表示在事件传播的捕获阶段注册监听器
冒泡:
- 浏览器检查实际点击的元素是否在冒泡阶段中注册了一个onclick事件处理程序,如果是,则运行它
- 然后它移动到下一个直接的祖先元素,并做同样的事情,然后是下一个,等等,直到它到达<html>元素
捕获则相反:
- 浏览器检查元素的最外层祖先,是否在捕获阶段中注册了一个onclick事件处理程序,如果是,则运行它
- 然后,它移动到中单击元素的下一个祖先元素,并执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素
当一个事件发生在具有父元素的元素上时,现代浏览器会运行这两个不同的阶段
因此,有时会出现不想发生的状况
<button>Display video</button>
<div class="hidden">
<video>
<source src="rabbit320.mp4" type="video/mp4">
<source src="rabbit320.webm" type="video/webm">
<p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
</video>
</div>
.hidden{visibility: hidden;}
.showing{visibility: visible;}
var btn = document.querySelector('button');
var videoBox = document.querySelector('div');
var video = document.querySelector('video');
btn.onclick = function() {
videoBox.setAttribute('class','showing');
};
videoBox.onclick = function() {
videoBox.setAttribute('class','hidden');
};
video.onclick = function() {
video.play();
};
由上面的代码,点击button视频会出现,当我们点击video想要播放视频时,事件在经过video处理后,会被传递到div,然后div设置为隐藏,video随着div消失了
那么怎样解决问题?
- 使用事件的stopPropagation()方法使事件停止传播
只需将上述js文件的video的事件处理修改为
video.onclick = function(e) {
e.stooPropagation();
video.play();
};
即可
事件冒泡及捕获的意义
当有多个子元素以类似的方式处理事件的时候,可以只为为它们的父级元素添加一个事件处理
一个很好的例子是一系列列表项,如果你想让每个列表点击时弹出一条信息,您可以将click单击事件监听器设置在父元素<div>上,它将会冒泡到列表项上
<div>
<span>1</span><span>2</span><span>3</span><br>
<span>4</span><span>5</span><span>6</span><br>
<span>7</span><span>8</span><span>9</span>
</div>
以下代码为父级元素添加事件处理,处理子元素的点击事件
function random(number) {
return Math.floor(Math.random()*(number+1));
}
var div = document.querySelector("div");
document.querySelector("div").addEventListener("click", function (e) {
if(!(e.target instanceof HTMLDivElement)){
e.target.style.backgroundColor = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
}
});
这种做法又形象地称为:事件委托
另 注意JS中的执行顺序
var div = document.querySelector("div").onclick = function(e){
div.setAttribute('class', 'hidden');
};
这里定义出的div实际上是后面的匿名函数,而非想要的div对象
JS真的太灵活了
div.onclick得到的是onclick方式注册的监听器
div.onclick()、div.onclick(e)则是调用这个事件处理函数。。
且div.onclick()访问不到addEventListener()
注册的事件处理
事件并不是JavaScript的核心部分——它们是在浏览器Web APIs中定义的
JavaScript在不同环境下使用不同的事件模型
2019/5/18