本章内容:使用跨文档消息传递、拖放API、音频与视频
HTML规范定义了很多新HTML 标记,为了匹配和这些标记的变化,HTML5规范也用显著篇幅定义了很多 JavaScript API。定义这些 API 的用意就是简化此前实现起来困难重重的任务,最终简化创建动态 Web 界面工作。
一、跨文档消息传递
跨稳定消息传递(cross-document messaging)简称 XDM。指的是在来自不同域的页面间传递消息。XDM把这种机制规范化,让我们能即稳妥又简单地实现跨文档通信。
XDM的核心是 postMessage() 方法。接受两个参数化:一条消息 和 一个表示消息接收方来自哪个域的字符串。第二个参数对保障安全完全通信非常重要,可以防止浏览器把消息发送到不安全的地方。
// 注意:所有支持 XDM 的浏览器也支持 iframe 的contentWindow 属性
var ifremaWidow = document.getElementById('myframe').contentWidow
ifemaWidow.postMessage('A secret', 'http://www.wrox.com')
如果传递给 postMessage() 的第二个参数是“*”,则可以表示消息发送给来自任何域的文档,但我们不推荐这么做
接受 XDM 消息时,会触发window 对象的message事件。这个事件是以异步形式触发的,因此从发送消息到接收消息(触发接收窗口的 message 事件)可能要经过一段时间的延迟。触发 message 事件后,传递给 onmessage 处理程序的事件对象包含以下三方面的重要信息。
- data:作为 postMessage() 第一个参数传入的字符串数据。
- origin:发送消息的文档所在的域,例如:“http://www.wrox.com”
- source:发送消息的文档的 window 对象的代理。这个代理对象主要用于在发送上一条消息的窗口中调用 postMessage() 方法。如果发送消息的窗口来自同一个域,那这个对象就是 window。
接受到消息后验证发送窗口的来源是至关重要的,在 onmessage 处理程序中检测消息来源可以确保传入的消息来自已知的页面。
基本的检查模式如下:
window.onmessage = function(event) {
event = event || window.event
// 处理接收到的消息
processMessage(event.data)
// 可选:向来源窗口发送回执
event.source.postMessage('Received', 'http://p2p.wrox.com')
}
event.source 大多情况下只是 window 对象的代理,并非实际的window 对象。不能通过这个代理对象访问 window 对象的其他任何信息。通过这个代理调用 postMessage() 就好,这个方法永远存在,永远可以调用。
在通过 内嵌框架 加载其它域内容的内容时,使用XDM 是非常方便的。在混搭(mashup)和社交网络应用中,这种传递消息的方法即为常用。
二、原生拖放
最早啊在网页中引入 JavaScript 拖放功能的是 IE4。当时,网页中只有两种对象可以拖放:图像和某些文本。HTML5以 IE 的实例为基础制定了拖放规范。
2.1、拖放事件
拖动某元素时,会依次触发下列事件:
- dragstart
- 按下鼠标键并开始移动鼠标时触发
- drag
- 在元素被拖动期间会持续触发该事件。
- dragend
- 当拖动停止(无论是吧元素放到了有效的放置目标,还是放到了无效的放置目标上)
上诉三个事件的目标都是被拖动元素。默认情况下,浏览器不会在拖动期间改变被拖动元素的外观,但你可以自己修改。不过,大多浏览器都会为正被拖动的元素创建一个半透明的副本,这个副本始终跟随着光标移动。
当摸一个元素被拖动到一个有效的放置目标上时,下列事件会依次发生:
- dragenter
- 只要有元素拖动到放置目标上,就会触发
- dragover
- dragenter 之后触发,在被拖动的元素还在放置目标的范围内移动时,触发该事件
- dragleave 或 drop
- 如果元素被拖出了放置目标,触发 dragleave 事件
- 如果元素放到了放置目标中,则触发 drop 事件
2.2、自定义放置目标
在拖动元素经过某些无效放置目标时,可以看到一种特殊的光标(圆环中有一条反斜线)表示不能放置。可以把任何元素变成有效的放置目标,方法是重写 dragenter 和 dragover 事件的默认行为。
例如:假设有一个ID 为 droptarget 的 <div> 元素,可以用如下代码将它变成一个放置目标。
var droptarget = document.getElementById('droptarget')
droptarget.ondragover = function(event) {
event.preventDefault()
}
droptarget.ondragenter = function(event) {
event.preventDefault()
}
以上代码执行后,你就会发现当拖动着元素移动到放置目标上时,光标变成了允许放置的符号。当然,释放鼠标也会触发 drop 事件。
在 Firefox 3.5+中,放置事件的默认行为是打开被放置目标上的URL。为了让 Firefox 支持正常的拖放,还要取消 drop 事件的默认行为,阻止它打开 URL。
droptarget.ondrop = function(event) {
event.preventDefault()
}
2.3、dataTransfer 对象
为了在拖放操作时实现数据交换,IE5 引入了 dataTransfer 对象。它是事件处理对象的属性,只能在拖放事件处理程序中访问 dataTransfer 对象。可以使用这个对象的属性和方法来完善拖放功能。
dataTransfer 对象主要有两个方法:
- getDate():取得 setData() 保存的值。
- setData()
这两个参数都接受一个参数:是一个字符串,表示保存的数据类型,取值为 "text" 或 "URL",如下表示:
// 设置和接收文本数据
event.dataTransfer.setData("text", "some text")
var text = event.DataTransfer.getData("text")
// 设置和接收 URL
event.dataTransfer.setData("URL", "http://www.wrox.com")
var url = event.dataTransfer.getData("URL")
IE 只定义了 "text" 和 "URL" 两种有效的数据类型,而HTML5则对此加以扩展,允许指定各种 MIME类型。考虑到向后兼容,HTML5 也支持 "text" 和"URL",但是这两种类型会被映射为 "text/plain" 和 "text/rui-list"。dataTransfer 对象可以为每种 MIME 类型都保存一个值。保存在 dataTransfer 对象中的数据只能在 drop 事件处理程序中读取。
在拖动文本框中的文本时,浏览器会调用 setData() 方法,将拖动的文本以“text” 格式保存在 dataTransfer 对象中。作为开发人员,你也可以正在 dragstart 事件处理程序中调用 setData(),手工保存自己要传输的数据,以便将来使用。
Firefox 在其第5个 版本之前不能正确地将“url” 和 “text”映射为 “text/uri-list”和 “text/plain”。但是却能宝 “Text” 映射为“text/plain”。为了更好地在跨浏览器的情况下从 dataTransfer 对象取得数据,最好在取得 URL 数据时检测两个值,而在取得文本数据时使用“Text”
var dataTransfer = event.datatTransfer
// 读取 URL
var url = dataTransfer.getData('url') || dataTransfer.getData('text/uri-list')
// 读取文本
var text = dataTransfer.getData('Text')
2.4、dropEffect 与 effectAllowed
利用 dataTransfer 对象,不光是能够传输数据,还能通过它来确定被拖动的元素以及作为放置目标的元素 能接接收什么操作。为此,需要访问 dataTransfer 对象的两个属性:dropEffect 和 effectAllowed。
通过 dropEffect 属性可以知道被拖动的元素能够执行哪种放置行为。这个属性有下列4个可能的值:
- none:不能把拖动的元素放在这里。这是除文本框之外所有元素的默认值。
- move:应该把拖动的元素移动到放置目标。
- copy:应该把拖动的元素复制到放置目标。
- link:表示放置目标会打开拖动的元素(但拖动的元素必须是一个链接,有URL)
在把元素拖动到放置目标上时,以上每一个值都会导致光标显示为不同的符号,要使用 dropEffect属性,必须在 ondragenter 事件处理程序中针对放置目标来设置它
dropEffect 属性只有搭配 effectAllowed 属性才有用。effectAllowed 属性表示允许被拖动元素的哪种 dropEffect,effectAllowed 属性可能的值如下。
- uninitialize:没有给被拖动的元素设置 任何放置行为。
- none:被拖动大元素不能有任何行为
- copy:只允许值为 copy 的dropEffect
- link:只允许值为 link 的 dropEffect
- move:只允许值为 move 的 dropEffect
- copyLink:允许值为 copy 和 link 的 dropEffect
- copyMove:允许值为 copy 和 move 的 dropEffect
- linkMove:允许值为 link 和 move 的 dropEffect
- all:允许任意 dropEffect
必须在 ondragstart 事件处理程序中设置 effectAllowed 属性
。
2.5、可拖动
让其他元素(默认无法拖动的元素)可拖动也是可能的。 HTML5 为所有 HTML 元素规定了一个 draggable 属性,表示元素是否可以拖动。图像和连接的 draggable 属性自动被设置成了 true,而其他元素这个属性的默认值都是 false。
<!-- 让这个图像不可以拖动 -->
<img src="smile.gif" draggable="false" alt="Smiley face" />
<!-- 让这个元素可以拖动 -->
<div draggable="true">...</div>
另外,为了让 Firefox 支持可拖动属性,还必须添加一个 ondragstart 事件处理程序,并在 dataTransfer 对象中保存一些信息。
2.6、其他成员
HTML5 规范规定 dataTransfer 对象还应该包含下列方法 和 属性。
- addElement(element):为拖动操作添加一个元素。添加这个元素只能影响数据(即增加座位拖动源而响应回调的对象),不会影响拖动操作时页面元素的外观。
- clearData(format):清除以特定格式保存的数据。实现这个方法的浏览器有 IE、Firefox3.5+、Chrome 和 Safari4+。
- setDragImage(element, x ,y):指定一副图像,当拖动发生时,显示在光标下方。这个方法接受的三个参数分别是要显示的HTML 元素和光标在图像中的x,y坐标。其中,HTML元素可以是一副图像,也可以是其他元素。是图像则只显示图像,是其他元素则显示渲染后的元素。实现这个方法的浏览器有 Firefox3.5+、Safari4+、Chrome
- types:当前保存的数据类型。这是一个类似数组的集合,以“text”这个的字符串形式保存着数据类型。实现这个属性的浏览器有 IE10+、Firefox3.5+、Chrome
三、媒体元素
随着音频和视频在 Web 上的迅速流行,HTML5 新增了两个与媒体相关的标签,让开发人员不必依赖任何插件就能在网页中嵌入浏览器的音频和视频内容。这两个标签是 <audio> 和 <video>
<!-- 嵌入视频 -->
<video src="conference.mpg" id="myVideo">Video player not available. </video>
<!-- 嵌入音频 -->
<audio src="song.mp3" id="myAudio"> Audio player not available. </audio>
还可以设置 width 和 height 属性以指定视频播放器的大小,而为 poster 属性指定图像的 URI 可以在加载视频内容期间显示一副图像。另外,如果标签中有 controls 属性,则意味着浏览器应该显示 UI 空间,位于开始和借宿标签之间的任何内容都将作为后备内容,在浏览器不支持这两个媒体元素的情况下显示。
因为并非所有浏览器都支持所以媒体格式,所以可以指定多个不同的媒体来源。为此,不用在标签中指定src属性,而是要像下面这样使用一或多个 <source> 元素。
<!-- 嵌入视频 -->
<video id="myVideo">
<source src="conference.webm" type="video/webm; cpdes='vp8, vorbis'">
<source src="conference.ogv" type="video/webm; cpdes='theora, vorbis'">
<source src="conference.mpg">
video player not available
</video>
<!-- 嵌入音频 -->
<audio id="myAudio">
<source src="song.ogg" type="audio/ogg">
<source src="song.mp3" type="audio/mpeg">
Audio player not available
</audio>
3.1、属性
<video> 和 <audio> 元素都提供了完整的 JavaScript 接口。下表列出了这两个元素共有的属性,通过这些属性可以知道媒体的当前状态。
属性 | 数据类形 | 说明 |
---|---|---|
autoplay | 布尔值 | 取得或设置 autoplay标志 |
buffered | 时间范围 | 表示已下载的缓冲的事件范围的对象 |
bufferedBytes | 字节范围 | 表示已下载的缓冲的字节范围的对象 |
bufferingRate | 整数 | 下载过程中每秒钟平均接收到的位数 |
bufferingThrottled | 布尔值 | 表示浏览器是否对缓冲进行了节流 |
controls | 布尔值 | 取得或设置 controls 属性,用于显示或隐藏浏览器内置的控件 |
currentLoop | 整数 | 媒体文件已经循环的次数 |
currentSrc | 字符串 | 当前播放的媒体文件的URL |
currentTime | 浮点数 | 已经播放的秒数 |
defaultPlaybackRate | 浮点数 | 取得或设置默认的播放速度。默认值为 1.0秒 |
duration | 浮点数 | 媒体的总播放事件(秒数) |
ended | 布尔值 | 表示媒体文件是否播放完成 |
loop | 布尔值 | 取得或设置媒体文件在播放完成后是否再从头开始播放 |
muted | 布尔值 | 取得或设置媒体文件是否静音 |
networkState | 整数 | 表示当前媒体的网络连结状态:0表示空,1表示正在加载,2表示正在加载元数据,3表示已经加载了第一帧,4表示加载完成。 |
paused | 布尔值 | 表示播放器是否暂停 |
playbackRate | 浮点数 | 取得或设置当前的播放速度。用户可以改变这个值,让媒体播放速度变快或变慢,这与 defaultPlaybackRate 只能由开发人员修改的 defaultPlaybackRate 不同 |
played | 时间范围 | 到目前为止已经播放的事件范围 |
readyState | 整数 | 表示媒体是否已经就绪(可以播放了)。0表示数据不可用,1表示考科一显示当前怕,2表示可以开始播放,3表示媒体可以从头到尾播放 |
seekable | 时间范围 | 到目前为止已经播放的时间范围 |
seeking | 布尔值 | 表示播放器是否真正移动到媒体文件中的新位置 |
src | 字符串 | 媒体文件的来源。任何时候都可以重写这个属性 |
start | 浮点数 | 取得或设置媒体文件中开始播放的位置,以秒表示 |
start | 浮点数 | 取得或设置媒体文件中开始播放的位置,以秒表示 |
totalBytes | 整数 | 当前资源所需的总字节数 |
videoHeight | 整数 | 返回视频(不一定是元素)的高度。只适用于 <video> |
videoWidth | 整数 | 返回视频(不一定是元素)的宽度。只适用于 <video> |
volume | 浮点数 | 取得或设置当前音量,值为 0.0 到1.0 |
其中很多属性也可以直接在 <audio> 和 <video> 元素中设置
3.2、事件
处理大量属性之外,这两个媒体云阿苏还可以触发很多事件。这些事件监控着不同的属性的变化,这些变化可能是媒体播放的结果,也可能是用户操作播发器的结果。下表列出了媒体元素相关的事件。
事件 | 触发时机 |
---|---|
abort | 下载中断 |
canplay | 可以播放是;readyState值为2 |
canplaythrough | 播放可继续,而且应该不会中断;readyState值为3 |
canshowcurrentframe | 当前帧已经下载完成;readyState值为1 |
dataunavailable | 当前没有数据而不能播放;readyState 值为 0 |
durationchange | duration 属性的值改变 |
emptied | 网络连接关闭 |
empty | 发送错误阻止了媒体下载 |
ended | 媒体已播放到末尾,播放停止 |
error | 下载期间发生网络错误 |
load | 所有媒体已加载完成。这个事件可能会被废弃,建议使用 canplaythrough |
loadeddate | 媒体的第一帧以加载完成 |
loadedmetadata | 媒体的元数据已加载完成 |
loadstart | 下载已开始 |
pause | 播放已暂停 |
play | 媒体已接收到指令开始播放 |
playing | 媒体已实际开始播放 |
progress | 正在下载 |
ratechange | 播放媒体的速度改变 |
seeked | 搜索结束 |
seeking | 正移动到新位置 |
stakked | 浏览器尝试下载,但未接收到数据 |
timeupdate | currentTime被以不合理或意外的方式更新 |
volumechange | volume 属性值或 muted属性值已改变 |
waiting | 播放暂停,等待下载更多数据 |
这些事件之所以如此具体,就是为了让开发人员只使用少量 HTML 和 JavaScript(与创建 Flash 影片相比)即可编写出自定义的 音频/视频播放器。
3.3、自定义媒体播放器
使用 <audio> 和 <video> 元素的 play() 和 pause() 方法,可以手工控制媒体文件的播放。组合使用属性、事件和这两个方法,很容易可以创建一个自定义的媒体播放器。
<div class="mediaplayer">
<div class="video">
<video src="./video.mp4" poster="videoPoster.png" width="300" height="200" id="player">Video player not available</video>
</div>
<div class="controls">
<input type="button" value="play" id="video-btn">
<span id="curtime">0 </span>/ <span id="duration">0</span>
</div>
</div>
<script>
// 取得元素的引用
var player = document.getElementById('player'),
btn = document.getElementById('video-btn'),
curtime = document.getElementById('curtime'),
duration = document.getElementById('duration')
player.oncanplaythrough = function(event) { // 媒体文件加载完成
// 更新播放时间
duration.innerHTML = player.duration
// 为按钮添加事件处理程序
btn.onclick = function(event) {
console.log(player.paused)
if (player.paused) {
player.play()
btn.value = 'Pause'
} else {
player.pause()
btn.value = 'Play'
}
}
// 定时更新当前时间
setInterval (function () {
curtime.innerHTML = player.currentTime
}, 250)
}
3.4、检测编解码器的支持情况
并非所有浏览器都支持 <video> 和 <audio> 的所有编解码器,而这基本上就意味着你必须提供多个媒体来源。不过,也有一个 JavaScript API 能够检测浏览器是否支持某种格式和编辑器。这两个媒体元素都有一个 canPlayType() 方法,该方法接收一种格式/编解码字符串,返回“probably”、“maybe” 或 “”(空字符串)。
if (audio.canPlayType("audio/mpeg") {
}
如果给 canPlayType() 传入了一种 MIME 类型,则返回值可能是 "maybe" 或空字符串。这就是 因为媒体文件本身只不过是 音频或视频的一个容器,而真正决定文件能否播放的还是编码的格式。在同时传入 MIME 类型和编解码器的情况下,可能性就会增加,返回的字符串会变成"probably"。
var audio = dcoument.getElement('audio-player')
// 很有可能 "maybe"
if (audio.canPlayType('audio/mpeg') {
// todo
}
// 可能是 probably
if (audio.canPlayType('audio/ogg; codecs="vorbis"')) {
// todo
}
下标列出了已得到支持的音频格式和编解码器
音频 | 字符串 | 支持的浏览器 |
---|---|---|
AAC | audio/map4; codecs="mp4a.40.2" | IE9+、Safari4+、iOS版Safari |
MP3 | audio/mpeg | IE9+、Chrome |
Vorbis | audio/ogg; codecs="vorbis" | Firefox3.5+、Chrome、Opera 10.5+ |
WAV | audio/wav; codecs="1" | Firefox3.5+、Opera 10.5+、Chrome |
当然也可以使用 canPlayType() 来检测视频格式。下表列出了已知的得到支持的音频格式和编解码器
视频 | 字符串 | 支持的浏览器 |
---|---|---|
H.264 | video/mp4; codecs="avc1.42E01E, mp4a.40.2 | IE9+、Safari4+、iOS版Safari、Android版WebKit |
Theora | video/ogg; codecs="theora" | Firefox3.5+、Opera10.5、Chrome |
WebMvideo/webm; codecs="vp8, borbis" | Firefox4+、Opera10.6、Chrome |
3.5、Audio类型
<audio> 元素还有一个原生的 JavaScript 构造函数 Audio,可以在任何时候播放音频。从同为DOM元素的角度来看,Audio 和 Image 很相似,但 Auido 不用像 Image 那样必须插入到文档中,只要创建一个示例,宾川如音频源文件即可。
var audio = new Audio('sound.mp3')
audio.oncanplaythrough = function(event) {
audio.play()
}
创建新的 audio 实例即可开始下载指定的文件,下载完成后,调用 play() 就可以播放音频。
四、历史状态管理
历史状态管理是现代Web 应用 开发的一个难点。在Web应用中,用户每次操作不一定会打开一个全新的页面,因此“后退”和“前进”按钮也就失去了作用,导致用户很难在不同状态间切换。要解决这个问题,首先使用 hashchange 事件。HTML5 通过更新 history 对象为管理历史状态提供了方便
通过 hashchange 事件,可以知道 URL的参数什么时候发生了变化,即什么时候该有所反应。而通过状态管理 API,能够在不加载新页面的情况下改变浏览器的URL。为此,需要使用 history.pushState() 方法,该方法接三个参数:状态对象、新状态的标题、可选的相对URL。
history.pushState({name: 'Lee'}, 'Lee page', 'Lee.html')
执行 pushState() 方法后,新的状态信息就会被加入历史状态栈,而浏览器地址栏也会变成新的相对URL。
因为 pushState() 会创建新的历史状态,所以你会发现“后退”按钮也能使用了。按下“后退”按钮,会触发 window 对象的 popstate 事件。popstate 事件的事件对象有一个 state 属性,这个数据就包含着当初以第一个参数传递给 pushState() 的状态对象。
window.onpopstate = function(event) {
var state = event.state
if (state) { // 第一个页面加载时 state 为空
processState(state)
}
}
要更新当前状态,可以调用 replaceState(),传入的参数与 pushState() 的前两个参数相同。调用这个方法不会在历史状态栈中创建新状态,只会重写当前状态。
history.replaceState({name: "Greg"}, "greg's page")
五、小结
- 跨文档消息传递 API 能让我们在不降低同源策略安全性的前提下,在来自不同域的文档间传递消息。
- 原生拖动功能让我们可以方便的指定某个元素可拖动,并在操作系统要放置时做出响应,还可以创建自定义的可拖动元素及放置目标。
- 媒体元素 <audio> 和 <video> 拥有自己的与音频和视频交互的API。并非所有浏览器支持所有的媒体格式,因此应该使用 canPlayType() 检测浏览器是否支持特定的格式。
- 历史状态管理让我们不必卸载当前页面即可修改浏览器的历史状态栈。有了这种机制,用户就可以通过“后退” 和 “前进” 按钮在页面状态间切换,而这些状态完全由 JavaScript 进行控制。
网友评论