【笔记】JavaScript事件处理机制,元素大小判断与H5的媒体标签
前一阵舍友去面试,被问到JavaScript中的事件处理机制。暗自思忖,发现自己也没有深入的了解过。顺带连同常用的HTML元素大小和实际中用到的HTML5中的媒体元素简单整理在下面,方便之后回顾。
事件
JavaScript和HTML的交互是通过事件实现的。可以通过监听器订阅文档或窗口中的事件,在事件发生时执行特定的代码。这种属于设计模式中的观察者模式。
事件相关的API最早出现在IE4和NetScape Nivagator4(后面简称为网景)中。两种浏览器提供了相似却不同的API。在之后的DOM2级标准中对DOM事件进行了标准化。
事件流
事件流描述的是页面中接受时间的顺序。在这点上IE和网景采用了完全相反的两种处理思路。IE采用的是事件冒泡流,网景采用的是事件捕获流。
事件冒泡(event bubbling)指从事件开始的最具体的元素接收,再逐步向上传递到最外层的节点,直到document。如下图(来自红宝书)展示的过程,在下面的文档中:
1 |
|
如果div标签被点击,click事件会这样依次传递:<div> -> <body> -> <html> -> document。(不同浏览器实现细节上会有不同)

事件捕获(event capturing)则认为应该从父节点开始捕获事件直到事件目标。因此,同样的上面的例子,顺序将是:document -> <html> -> <body> -> <div>。

目前很少有人使用事件捕获这种方式作为事件流。
DOM 事件流
“DOM2级标准”中规定事件流包括三个阶段,事件捕获、处于目标、时间冒泡。在实际的DOM事件流中,实际目标不会接受到事件。因此如下图展示的那样,捕获阶段停止在父目标<body>上,之后事件发生在目标上,并作为事件冒泡的一部分。然后,冒泡阶段发生,事件传回到文档。

事件处理程序
事件处理程序指用户指定响应事件的某种动作。它们都以’on’开头。HTML元素本身都可以使用与之同名的HTML特性。
DOM0级事件处理程序
DOM0级事件处理程序就是将一个函数直接赋值给一个事件处理程序属性。使用这种方法指定的事件处理程序被认为是元素的一种方法,从而其作用域为元素本身,即this指向引用元素。可以通过直接为事件处理程序属性赋值为null删除。
1 | var btn = document.getElementById('button'); |
所有浏览器都支持DOM0级事件处理程序。这么做的好处是可以保证浏览器兼容性,缺点是使得HTML和JavaScript紧密耦合,不利用后期维护。
DOM2级事件处理程序
伴随DOM2级标准提出,“DOM2级事件”提出了两种方法,用于绑定和解除事件处理程序:addEventListener()和removeEventListener()。它接受3个参数:事件名、事件处理程序对应的函数、表示捕获阶段的布尔值。
1 | var btn = document.getElementById('button'); |
使用DOM2级方法绑定事件处理程序的一个优点是,可以添加多个程序到同一个标签上。使用DOM0级方法时则会覆盖上一次的事件处理程序。IE9及以上版本都支持DOM2级事件处理程序。
由于IE事件处理程序在IE8之前,是通过类似的attachEvent()和detachEvent()方法。它的第一个参数是事件名(需要带上on),第二个参数是事件处理程序。通过这种方法绑定的处理程序都添加在冒泡阶段,且需要注意的是其中的this等于window对象。支持这种方式有IE和Opera。
因此,一个跨浏览器兼容的事件绑定和解绑应该是下面这样的:
1 | var EventUtil = { |
===
元素大小与位置
这些属性方法并不属于“DOM2级样式”,但是却经常得到使用。目前所有主流浏览器都支持这些属性。它们大多都是只读的。
偏移量
偏移量描述元素在屏幕中占用的可用空间,由其宽高决定,包括内边距、滚动条和边框(不包括外边距)。有下面4个属性:
offsetHeight元素垂直方向上的占用空间offsetWidth元素水平方向上的占用空间offsetLeft元素左边框距offsetParent元素左内边框的像素距离offsetTop元素上边框距offsetParent元素上内边框的像素距离
可以利用元素的offsetLeft或offsetLeft与其offsetParent对应属性相加直到根元素,获取到元素相对于页面的左偏移值或上偏移值。
客户区大小
客户区大小指元素内容和内边距占据的空间大小,不包括滚动条。clientWidth是元素内容宽度加左右内边距的宽度,clientHeight是元素内容高度加上下内边距的高度。
可以通过对body元素取值来获取当前浏览器视口的大小。
滚动大小
滚动大小包含滚动内容的元素大小。它有下面4个相关属性:
scrollHeight没有滚动条时,元素内容的高度scrollWidth没有滚动条时,元素内容的宽度scrollLeft被隐藏在内容区域左侧的像素数,可以设置从而改变元素滚动位置scrollTop被隐藏在内容区域上侧的像素数,可以设置从而改变元素滚动位置
scrollHeight/scrollWidth和clientHeight/clientWidth在不同浏览器下的表现行为并不相同,有的表示视口大小,有的表示元素内容区域大小。使用时可以取较大值。而另外两个属性scrollLeft和scrollTop则通常用在document中,获取和滚动相关的属性。
确定元素大小
大多数主流浏览器为元素提供了getBoundingClientRect()方法,返回一个对象,包含left,right,top,bottom四个属性。给出了元素相对于视口的位置。
对不支持这个方法的浏览器,可以通过偏移量的相关属性获取。
===
媒体元素
HTML5出现前,提供富媒体内容的网站多采用Flash的方式保证浏览器兼容性。HTML5新增了两个标签<audio>和<video>。用于方便地嵌入音频和视频内容。同时,这两个标签也提供了实现常用功能的JavaScript API。允许为媒体创建自定义控件。
1 | <video src="demo.mpg" id="foo">Video player is not available.</video> |
其中元素的src属性指定了加载的媒体文件,还可以通过width和height属性指定播放器大小。controls属性意味浏览器应该显示UI控件用于操作媒体。标签中的内容用于在不支持时显示后备内容。
因为不同浏览器支持的媒体格式集并不完全相同,可以在标签下指定一或多个<source>元素,通过src和type属性指定来源和格式,视频标签下<source>的type中甚至可以指定codecs表示解码器。目前现代浏览器(IE9+,对IE说的就是你)都支持这两个标签。
1 | <video id="myVideo"> |
属性
<video>和<audio>提供了完善的JavaScript接口,下面是一些可能会用到的它们的属性。其中很多可以直接在标签元素上设置。
autoplay取消或设置当前autoplay标识controls取消或设置当前controls标识,用于显示和隐藏浏览器内置控件currentTime获取已经播放的秒数duration获取媒体的总长度(秒数)ended获取媒体是否播放完成loop取消或设置媒体文件是否循环播放muted取消或设置媒体文件是否静音paused标识播放器是否暂停playbackRate取消或设置当前播放速度readyState标识媒体是否就绪,有0,1,2,3四种情况,表示不可用、可以播放当前帧、可以播放、加载完毕src媒体文件来源,可重写volume取消或设置当前音量,值为0.0到1.0
事件
这两个媒体元素还有许多事件,有的是媒体播放的结果,有的是用户操作的结果。
abort下载中断canplay对应着readyState为2canplaythrough对应着readyState为3ended媒体播放完毕error下载过程网络错误pause播放暂停play媒体收到播放指令playing媒体开始播放ratechange播放速度改变seeked移动到新位置seeking正在移动进度条volumnchangevolumn和muted属性值改变waiting播放因下载未完成暂停
在如此丰富的属性和事件的帮助下,结合play()和pause()方法,我们可以很容易构建一个自定义的媒体播放器。
1 | <div class="player"> |
1 | var player = document.getElementById("video"), |
最后,不是所有浏览器都支持这两个标签的所有解码器,因此有一个API来检测浏览器是否支持某种解码器。通过canPlayType()方法,该方法接收格式/编解码器(如”audio/wav“)字符串,返回”probably”, “maybe”或是空字符串””。像下面这样:
1 | if (audio.canPlayType("audio/mpeg")){ |