一、跨文档消息传递(XDM)
1.发送消息
postMessage(msg,domain)用于发送跨文档消息。第一个参数是要传递的消息内容,第二个参数表示接收方来自哪个域。第二个参数有助于提高安全性,如果发现域不匹配则不会进行操作。
2.接收消息
接收到消息时,会触发window对象的message事件。传递给onmessage事件处理程序的对象主要包含三个信息:
data:对应postMessage()第一个参数传入的内容;
origin:发送消息的文档所在域。接收到消息后,检测消息来源是非常重要的步骤。
source:发送消息的文档的window对象代理。
实例:
outer.html:
<body>
<span id="msg"></span>
<input type="text"id="input">
<input type="button"id="submit"value="提交">
<iframe width="300"height="150"src="inner.html"id="frame"></iframe>
<script type="text/javascript"src="EventUtil.js"></script>
<script type="text/javascript">
varbtn =document.getElementById('submit'); //向iframe发送消息
EventUtil.addHandler(btn, 'click', function(event) { varinput =document.getElementById('input') .value; variframe =document.getElementById('frame') .contentWindow; if(iframe.postMessage) { iframe.postMessage(input, '*'
);
}
//else{ alert('do not support postMessage'); } }); 接收iframe发送的消息
EventUtil.addHandler(window,'message',function(event){
</scriptvarmsg =document.getElementById('msg'); msg.innerHTML=event.data; }); >
</body>
inner.html:
<body> <span id="msg"></span> <input type="text" id="input"> <input type="button" id="submit" value="提交"> <script type="text/javascript" src="EventUtil.js"></script> <script type="text/javascript"> varmsg = document.getElementById('msg');
//接收外层主窗体发送的消息 EventUtil.addHandler(window, 'message', function(event){ msg.innerHTML =event.data; }); varsubmit=document.getElementById("submit");
//向外层主窗体发送消息 EventUtil.addHandler(submit,'click',function(event){ if(parent.postMessage) { varinput = document.getElementById('input').value; parent.postMessage(input, '*'
);
}
else{ alert('do not support postMessage'); } }); </script> </body>
二、拖放
1. 拖放事件
被拖动元素会依次触发以下事件:
dragstart:按下鼠标键并开始移动时触发;
drag:在元素被拖动期间会持续触发该事件;
dragend:拖动停止时触发。
当元素拖动到一个目标元素时,会依次触发以下事件:
dragenter:元素被拖动到目标元素上时触发。
dragover:被拖动的元素在目标元素范围内移动时会持续触发。
dragleave或drop:当被拖动元素离开目标元素时触发dragleave。如果元素放到了目标元素中,则触发drop而不在触发dragleave。
虽然大多数元素支持拖放的目标元素事件,但是这些元素默认是不允许放置的。所以要使用preventDefault()取消这些元素的默认事件。
<img src="http://static.cnblogs.com/images/logo_small.gif" id="img" > <div id="target" style="margin-right:10px;border:1px solid black; width:100px; height:100px;"></div> <script type='text/javascript' src="EventUtil.js"></script> <script type='text/javascript'> varimage = document.getElementById("img"); varhandlerImg=function(event){ event=EventUtil.getEvent(event); console.log(event.type); } EventUtil.addHandler(image, "dragstart", handlerImg); EventUtil.addHandler(image, "drag", handlerImg); EventUtil.addHandler(image, "dragend", handlerImg); varhandler=function(event){ event=
EventUtil.getEvent(event); event.preventDefault();
console.log(event.type); }
vartarget=document.getElementById("target"); EventUtil.addHandler(target,"dragenter",handler); EventUtil.addHandler(target,"dragover",handler); EventUtil.addHandler(target,"dragleave",handler); EventUtil.addHandler(target,"drop",handler);
</script>
2.dataTransfer对象
dataTransfer对象是事件对象的一个属性,用于从被拖动的元素向目标元素传递字符串类型的数据。保存在dataTransfer对象中的数据只能在ondrop事件中读取。几个常用方法:
setData(type,value):设置用来设置数据。第一个参数是“Text”或者“url”,第二个参数是字符串类型的数据。text和url类型还是略有区别的,如果将数据保存为url,那么浏览器会把它当作网页中的链接。如果将其拖放到浏览器窗口中,浏览器就会打开该链接。
getData(type):参数type是"Text"或“url”,与set中的设置保持一致。
setDragImage(target,x,y):指定一幅图像,当拖动发生时,显示在光标下方。
<img src="http://static.cnblogs.com/images/logo_small.gif"id="img">
<div id="target"style="float:left;margin-left:300px;border:1px solid black; width:200px; height:100px;"></div>
<script type='text/javascript' src="EventUtil.js"></script>
<script type='text/javascript'>
varimage =document.getElementById('img');
varhandlerImg =function(event) { event =EventUtil.getEvent(event); vartarget =EventUtil.getTarget(event); event.dataTransfer.setData('Text', target.id); event.dataTransfer.setData('url',target.getAttribute('src'
));
event.dataTransfer.setDragImage(target,
'dragstart'10,10); } EventUtil.addHandler(image, , handlerImg);
varhandler =function(event) { event =EventUtil.getEvent(event); vartarget =EventUtil.getTarget(event); switch(event.type) { case'drop': event.preventDefault(); varid =event.dataTransfer.getData('Text'); varurl=event.dataTransfer.getData('url'
);
target.appendChild(document.getElementById(id)); target.innerHTML
break+=url; ; case'dragover': event.preventDefault(); break; } }
vartarget =document.getElementById('target'); EventUtil.addHandler(target, 'dragover', handler); EventUtil.addHandler(target, 'drop', handler);
</script>
3.可拖放
默认情况下,图像、文本等是可以拖动的,大多数其他元素不能拖动。如果想让某个元素可拖动,只需设置draggable=true即可。
<div id="target"style="border:1px solid black; width:200px; height:100px;"draggable="false"></div>
三、媒体元素
1.音频和视频控件
<audio id="player"src="billyBrowsers.ogg"poster="smile.gif"controls>不支持音频</audio>
<video id="player"src="billyBrowsers.ogg"poster="mymovie.jpg"width="300"height="200">不支持视频</video>
2.常用属性
autoplay:布尔型,设置是否自动播放。
controls:布尔型,用来设置浏览器中的自带控件是否显示。
currentSrc:当前播放的媒体文件url。
duration:媒体的总播放时间。
currentTime:当前已经播放的秒数。
loop:布尔型,媒体是否自动循环播放。
plaused:媒体播放器是否暂停。
src:媒体文件的来源。
<div class="mediaplayer">
<div class="video">
<audio id="player"src="billyBrowsers.ogg"poster="smile.gif"controls autoplay='true'>不支持音频</audio>
</div>
<div class="controls">
<input type="button"value="Play"id="audio-btn"/>
<span id="curtime">0</span>/<span id="duration">0</span>
</div>
</div>
<script>
window.onload =function(){ varplayer =document.getElementById("player"); varbtn =document.getElementById("audio-btn"); varcurtime =document.getElementById("curtime"); varduration =document.getElementById("duration"); duration.innerHTML =player.duration; EventUtil.addHandler(btn, "click", function(event){ if
(player.paused){ console.log(player.currentSrc); player.play();
btn.value
else="Pause"; }
{ player.pause();
btn.value
="Play"; } }); setInterval(function(){ curtime.innerHTML =
player.currentTime;
},
</250); }; script>
3.Audio类型
<audio>元素有对应的构造函数Audio。其实不必将Audio插入到文档中,在创建Audio实例后就开始下载指定文件,下载完成后,调用play()方法就可以开始播放。
<script>
window.onload =function(){ varplayer=newAudio("billyBrowsers.ogg"); EventUtil.addHandler(player,"canplay",function
(){ player.play();
}) };
</script>
16.1 跨文档消息传递 跨文档消息传送(cross-document messaging),有时候简称为XDM,指的是在来自不同域的页面间传递消息。例如,www.wrox.com域中的页面与位于一个内嵌框架中的p2p.wrox.com域中的页面通信。在 XDM机制出现之前,要稳妥地实现这种通信需要花很多工夫。XDM把这种机制规范化,让我们能既稳妥又简单地实现跨文档通信。 XDM的核心是postMessage()方法。在HTML5规范中,除了XDM部分之外的其他部分也会提到这个方法名,但都是为了同一个目的:向另一个地方传递数据。对于XDM而言,“另一个地方”指的是包含在当前页面中的<iframe>元素,或者由当前页面弹出的窗口。 postMessage()方法接收两个参数:一条消息和一个表示消息接收方来自哪个域的字符串。第二个参数对保障安全通信非常重要,可以防止浏览器把消息发送到不安全的地方。来看下面的例子。 //注意:所有支持XDM的浏览器也支持iframe的contentWindow属性 var iframeWindow = document.getElementById("myframe").contentWindow; iframeWindow.postMessage("A secret", "http://www.wrox.com"); 最后一行代码尝试向内嵌框架中发送一条消息,并指定框架中的文档必须来源于"http:// www.wrox.com"域。如果来源匹配,消息会传递到内嵌框架中;否则,postMessage()什么也不做。这一限制可以避免窗口中的位置在你不知情的情况下发生改变。如果传给postMessage()的第二个参数是"*",则表示可以把消息发送给来自任何域的文档,但我们不推荐这样做。 接收到XDM消息时,会触发window对象的message事件。这个事件是以异步形式触发的,因此从发送消息到接收消息(触发接收窗口的message事件)可能要经过一段时间的延迟。触发message事件后,传递给onmessage处理程序的事件对象包含以下三方面的重要信息。 data:作为postMessage()第一个参数传入的字符串数据。 origin:发送消息的文档所在的域,例如"http://www.wrox
source:发送消息的文档的window对象的代理。这个代理对象主要用于在发送上一条消息的窗口中调用 postMessage()方法。如果发送消息的窗口来自同一个域,那这个对象就是window。 接收到消息后验证发送窗口的来源是至关重要的。就像给postMessage()方法指定第二个参数,以确保浏览器不会把消息发送给未知页面一样,在onmessage 处理程序中检测消息来源可以确保传入的消息来自已知的页面。基本的检测模式如下。 EventUtil.addHandler(window, "message", function(event){ //确保发送消息的域是已知的域 if (event.origin == "http://www.wrox.com"){ //处理接收到的数据 processMessage(event.data); //可选:向来源窗口发送回执 event.source.postMessage("Received!", "http://p2p.wrox.com"); } }); 还是要提醒大家,event.source大多数情况下只是window对象的代理,并非实际的window对象。换句话说,不能通过这个代理对象访问window对象的其他任何信息。记住,只通过这个代理调用postMessage()就好,这个方法永远存在,永远可以调用。 XDM还有一些怪异之处。首先,postMessage()的第一个参数最早是作为“永远都是字符串”来实现的。但后来这个参数的定义改了,改成允许传入任何数据结构。可是,并非所有浏览器都实现了这一变化。为保险起见,使用postMessage()时,最好还是只传字符串。如果你想传入结构化的数据,最佳选择是先在要传入的数据上调用JSON.stringify(),通过 postMessage()传入得到的字符串,然后再在onmessage 事件处理程序中调用JSON.parse()。 在通过内嵌框架加载其他域的内容时,使用XDM是非常方便的。因此,在混搭(mashup)和社交网络应用中,这种传递消息的方法极为常用。有了XDM,包含<iframe>的页面可以确保自身不受恶意内容的侵扰,因为它只通过XDM与嵌入的框架通信。而XDM也可以在来自相同域的页面间使用。 支持XDM的浏览器有IE8+、Firefox 3.5+、Safari 4+、Opera、Chrome、iOS版 Safari及 Android版WebKit 。XDM 已经作为一个规范独立出来,现在它的名字叫 Web Messaging,官方页面是http://dev.w3.org/html5/postmsg/。 16.2 原生拖放 最早在网页中引入 JavaScript 拖放功能的是 IE4。当时,网页中只有两种对象可以拖放:图像和某些文本。拖动图像时,把鼠标放在图像上,按住鼠标不放就可以拖动它。拖动文本时,要先选中文本,然后可以像拖动图像一样拖动被选中的文本。在IE 4中,唯一有效的放置目标是文本框。到了IE5,拖放功能得到扩展,添加了新的事件,而且几乎网页中的任何元素都可以作为放置目标。IE5.5更进一步,让网页中的任何元素都可以拖放。(IE6同样也支持这些功能。)HTML5以IE的实例为基础制定了拖放规范。Firefox 3.5、Safari 3+和 Chrome也根据 HTML5规范实现了原生拖放功能。 说到拖放,最有意思的恐怕就是能够在框架间、窗口间,甚至在应用间拖放网页元素了。浏览器对拖放的支持为实现这些功能提供了便利
16.2.1 拖放事件 通过拖放事件,可以控制拖放相关的各个方面。其中最关键的地方在于确定哪里发生了拖放事件,有些事件是在被拖动的元素上触发的,而有些事件是在放置目标上触发的。拖动某元素时,将依次触发下列事件: (1) dragstart (2) drag (3) dragend 按下鼠标键并开始移动鼠标时,会在被拖放的元素上触发dragstart事件。此时光标变成“不能放”符号(圆环中有一条反斜线),表示不能把元素放到自己上面。拖动开始时,可以通过 ondragstart事件处理程序来运行JavaScript代码。 触发dragstart 事件后,随即会触发drag 事件,而且在元素被拖动期间会持续触发该事件。这个事件与mousemove事件相似,在鼠标移动过程中,mousemove事件也会持续发生。当拖动停止时(无论是把元素放到了有效的放置目标,还是放到了无效的放置目标上),会触发dragend事件。 上述三个事件的目标都是被拖动的元素。默认情况下,浏览器不会在拖动期间改变被拖动元素的外观,但你可以自己修改。不过,大多数浏览器会为正被拖动的元素创建一个半透明的副本,这个副本始终跟随着光标移动。 当某个元素被拖动到一个有效的放置目标上时,下列事件会依次发生: (1) dragenter (2) dragover (3) dragleave 或 drop 只要有元素被拖动到放置目标上,就会触发dragenter事件(类似于mouseover事件)。紧随其后的是dragover 事件,而且在被拖动的元素还在放置目标的范围内移动时,就会持续触发该事件。如果元素被拖出了放置目标,dragover 事件不再发生,但会触发dragleave 事件(类似于 mouseout事件)。如果元素被放到了放置目标中,则会触发drop事件而不是dragleave事件。上述三个事件的目标都是作为放置目标的元素。 16.2.2 自定义放置目标 在拖动元素经过某些无效放置目标时,可以看到一种特殊的光标(圆环中有一条反斜线),表示不能放置。虽然所有元素都支持放置目标事件,但这些元素默认是不允许放置的。如果拖动元素经过不允许放置的元素,无论用户如何操作,都不会发生drop事件。不过,你可以把任何元素变成有效的放置目标,方法是重写dragenter和dragover事件的默认行为。例如,假设有一个ID为"droptarget"的<div>元素,可以用如下代码将它变成一个放置目标。 var droptarget = document.getElementById("droptarget"); EventUtil.addHandler(droptarget, "dragover", function(event){ EventUtil.preventDefault(event); }); EventUtil.addHandler(droptarget, "dragenter", function(event){ EventUtil.preventDefault(event); });
以上代码执行后,你就会发现当拖动着元素移动到放置目标上时,光标变成了允许放置的符号。当然,释放鼠标也会触发drop事件。 在Firefox 3.5+中,放置事件的默认行为是打开被放到放置目标上的URL。换句话说,如果是把图像拖放到放置目标上,页面就会转向图像文件;而如果是把文本拖放到放置目标上,则会导致无效URL错误。因此,为了让Firefox支持正常的拖放,还要取消drop事件的默认行为,阻止它打开URL: EventUtil.addHandler(droptarget, "drop", function(event){ EventUtil.preventDefault(event); }); 16.2.3 dataTransfer对象 只有简单的拖放而没有数据变化是没有什么用的。为了在拖放操作时实现数据交换,IE 5 引入了dataTransfer 对象,它是事件对象的一个属性,用于从被拖动元素向放置目标传递字符串格式的数据。因为它是事件对象的属性,所以只能在拖放事件的事件处理程序中访问 dataTransfer 对象。在事件处理程序中,可以使用这个对象的属性和方法来完善拖放功能。目前,HTML5 规范草案也收入了dataTransfer 对象。 dataTransfer 对象有两个主要方法:getData()和 setData()。不难想象,getData()可以取得由setData()保存的值。setData()方法的第一个参数,也是getData()方法唯一的一个参数,是一个字符串,表示保存的数据类型,取值为"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/uri-list"。 实际上,dataTransfer 对象可以为每种 MIME 类型都保存一个值。换句话说,同时在这个对象中保存一段文本和一个URL不会有任何问题。不过,保存在dataTransfer对象中的数据只能在drop事件处理程序中读取。如果在ondrop 处理程序中没有读到数据,那就是dataTransfer 对象已经被销毁,数据也丢失了。 在拖动文本框中的文本时,浏览器会调用 setData()方法,将拖动的文本以"text"格式保存在dataTransfer 对象中。类似地,在拖放链接或图像时,会调用setData()方法并保存URL。然后,在这些元素被拖放到放置目标时,就可以通过 getData()读到这些数据。当然,作为开发人员,你也可以在dragstart 事件处理程序中调用setData(),手工保存自己要传输的数据,以便将来使用。 将数据保存为文本和保存为 URL是有区别的。如果将数据保存为文本格式,那么数据不会得到任何特殊处理。而如果将数据保存为URL,浏览器会将其当成网页中的链接。换句话说,如果你把它放置到另一个浏览器窗口中,浏览器就会打开该URL。 Firefox 在其第 5 个版本之前不能正确地将"url"和"text"映射为"text/uri-list"和"text/plain"。但是却能把"Text"(T大写)映射为"text/plain"。
下从 dataTransfer 对象取得数据,最好在取得 URL 数据时检测两个值,而在取得文本数据时使用"Text"。 var dataTransfer = event.dataTransfer; //读取URL var url = dataTransfer.getData("url") ||dataTransfer.getData("text/uri-list"); //读取文本 var text = dataTransfer.getData("Text"); DataTransferExample01.htm 注意,一定要把短数据类型放在前面,因为IE 10及之前的版本仍然不支持扩展的MIME类型名,而它们在遇到无法识别的数据类型时,会抛出错误。 16.2.4 dropEffect与effectAllowed 利用 dataTransfer 对象,可不光是能够传输数据,还能通过它来确定被拖动的元素以及作为放置目标的元素能够接收什么操作。为此,需要访问dataTransfer 对象的两个属性:dropEffect 和effectAllowed。 其中,通过 dropEffect 属性可以知道被拖动的元素能够执行哪种放置行为。这个属性有下列 4个可能的值。 "none":不能把拖动的元素放在这里。这是除文本框之外所有元素的默认值。 "move":应该把拖动的元素移动到放置目标。 "copy":应该把拖动的元素复制到放置目标。 "link":表示放置目标会打开拖动的元素(但拖动的元素必须是一个链接,有URL)。 在把元素拖动到放置目标上时,以上每一个值都会导致光标显示为不同的符号。然而,要怎样实现光标所指示的动作完全取决于你。换句话说,如果你不介入,没有什么会自动地移动、复制,也不会打开链接。总之,浏览器只能帮你改变光标的样式,而其他的都要靠你自己来实现。要使用dropEffect属性,必须在ondragenter事件处理程序中针对放置目标来设置它。 dropEffect 属性只有搭配 effectAllowed 属性才有用。effectAllowed 属性表示允许拖动元素的哪种dropEffect,effectAllowed 属性可能的值如下。 "uninitialized":没有给被拖动的元素设置任何放置行为。 "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属性。
假设你想允许用户把文本框中的文本拖放到一个<div>元素中。首先,必须将 dropEffect 和effectAllowed 设置为"move"。但是,由于<div>元素的放置事件的默认行为是什么也不做,所以文本不可能自动移动。重写这个默认行为,就能从文本框中移走文本。然后你就可以自己编写代码将文本插入到<div>中,这样整个拖放操作就完成了。如果你将dropEffect和effectAllowed的值设置为"copy",那就不会自动移走文本框中的文本。 Firefox 5 及之前的版本在处理effectAllowed 属性时有一个问题,即如果你在代码中设置了这个属性的值,那不一定会触发drop事件。 16.2.5 可拖动 默认情况下,图像、链接和文本是可以拖动的,也就是说,不用额外编写代码,用户就可以拖动它们。文本只有在被选中的情况下才能拖动,而图像和链接在任何时候都可以拖动。 让其他元素可以拖动也是可能的。HTML5为所有HTML元素规定了一个draggable属性,表示元素是否可以拖动。图像和链接的draggable属性自动被设置成了true,而其他元素这个属性的默认值都是false。要想让其他元素可拖动,或者让图像或链接不能拖动,都可以设置这个属性。例如: <!-- 让这个图像不可以拖动 --> <img src="smile.gif" draggable="false" alt="Smiley face"> <!-- 让这个元素可以拖动 --> <div draggable="true">...</div> 支持draggable 属性的浏览器有IE 10+、Firefox 4+、Safari 5+和 Chrome。Opera 11.5及之前的版本都不支持HTML5的拖放功能。另外,为了让Firefox支持可拖动属性,还必须添加一个ondragstart事件处理程序,并在dataTransfer对象中保存一些信息。 在IE9及更早版本中,通过mousedown 事件处理程序调用dragDrop()能够让任何元素可拖动。而在 Safari 4 及之前版本中,必须额外给相应元素设置 CSS 样式–khtml-user-drag: element。 16.2.6 其他成员 HTML5规范规定dataTransfer 对象还应该包含下列方法和属性。 addElement(element):为拖动操作添加一个元素。添加这个元素只影响数据(即增加作为拖动源而响应回调的对象),不会影响拖动操作时页面元素的外观。在写作本书时,只有Firefox 3.5+实现了这个方法。 clearData(format):清除以特定格式保存的数据。实现这个方法的浏览器有IE、Fireforx 3.5+、Chrome和 Safari 4+。 setDragImage(element, x, y):指定一幅图像,当拖动发生时,显示在光标下方。这个方
法接收的三个参数分别是要显示的HTML元素和光标在图像中的x、y坐标。其中,HTML元素可以是一幅图像,也可以是其他元素。是图像则显示图像,是其他元素则显示渲染后的元素。实现这个方法的浏览器有Firefox 3.5+、Safari 4+和Chrome。 types:当前保存的数据类型。这是一个类似数组的集合,以"text"这样的字符串形式保存着数据类型。实现这个属性的浏览器有IE10+、Firefox 3.5+和Chrome。 16.3 媒体元素 随着音频和视频在 Web 上的迅速流行,大多数提供富媒体内容的站点为了保证跨浏览器兼容性,不得不选择使用Flash。HTML5新增了两个与媒体相关的标签,让开发人员不必依赖任何插件就能在网页中嵌入跨浏览器的音频和视频内容。这两个标签就是<audio>和<video>。 这两个标签除了能让开发人员方便地嵌入媒体文件之外,都提供了用于实现常用功能的 JavaScript API,允许为媒体创建自定义的控件。这两个元素的用法如下。 <!-- 嵌入视频 --> <video src="conference.mpg" id="myVideo">Video player not available.</video> <!-- 嵌入音频 --> <audio src="song.mp3" id="myAudio">Audio player not available.</audio> 使用这两个元素时,至少要在标签中包含src 属性,指向要加载的媒体文件。还可以设置width和height 属性以指定视频播放器的大小,而为poster属性指定图像的URI可以在加载视频内容期间显示一幅图像。另外,如果标签中有controls 属性,则意味着浏览器应该显示UI控件,以便用户直接操作媒体。位于开始和结束标签之间的任何内容都将作为后备内容,在浏览器不支持这两个媒体元素的情况下显示。 因为并非所有浏览器都支持所有媒体格式,所以可以指定多个不同的媒体来源。为此,不用在标签中指定src属性,而是要像下面这样使用一或多个<source>元素。 <!-- 嵌入视频 --> <video id="myVideo"> <source src="conference.webm" type="video/webm; codecs='vp8, vorbis'"> <source src="conference.ogv" type="video/ogg; codecs='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> 关于视频和音频编解码器的内容超出了本书讨论的范围。作者在此只想告诉大家,不同的浏览器支持不同的编解码器,因此一般来说指定多种格式的媒体来源是必需的。支持这两个媒体元素的浏览器有IE9+、Firefox 3.5+、Safari 4+、Opera 10.5+、Chrome、iOS版 Safari和 Android版 WebKit。
16.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 浮点数 取得或设置媒体文件中开始播放的位置,以秒表示 totalBytes 整数 当前资源所需的总字节数 videoHeight 整数 返回视频(不一定是元素)的高度。只适用于<video> videoWidth 整数 返回视频(不一定是元素)的宽度。只适用于<video> volume 浮点数 取得或设置当前音量,值为0.0到1.0 其中很多属性也可以直接在<audio>和<video>元素中设置。
16.3.2 事件 除了大量属性之外,这两个媒体元素还可以触发很多事件。这些事件监控着不同的属性的变化,这些变化可能是媒体播放的结果,也可能是用户操作播放器的结果。下表列出了媒体元素相关的事件。 事 件 触发时机 abort 下载中断 canplay 可以播放时;readyState值为2 canplaythrough 播放可继续,而且应该不会中断;readyState值为3 canshowcurrentframe 当前帧已经下载完成;readyState值为1 dataunavailable 因为没有数据而不能播放;readyState值为0 durationchange duration属性的值改变 emptied 网络连接关闭 empty 发生错误阻止了媒体下载 ended 媒体已播放到末尾,播放停止 error 下载期间发生网络错误 load 所有媒体已加载完成。这个事件可能会被废弃,建议使用canplaythrough loadeddata 媒体的第一帧已加载完成 loadedmetadata 媒体的元数据已加载完成 loadstart 下载已开始 pause 播放已暂停 play 媒体已接收到指令开始播放 playing 媒体已实际开始播放 progress 正在下载 ratechange 播放媒体的速度改变 seeked 搜索结束 seeking 正移动到新位置 stalled 浏览器尝试下载,但未接收到数据 timeupdate currentTime被以不合理或意外的方式更新 volumechange volume属性值或muted属性值已改变 waiting 播放暂停,等待下载更多数据 这些事件之所以如此具体,就是为了让开发人员只使用少量HTML和JavaScript(与创建Flash影片相比)即可编写出自定义的音频/视频播放器。 16.3.3 自定义媒体播放器 使用<audio>和<video>元素的play()和pause()方法,可以手工控制媒体文件的播放。组合使用属性、事件和这两个方法,很容易创建一个自定义的媒体播放器,如下面的例子所示。
<div class="mediaplayer"> <div class="video"> <video id="player" src="movie.mov" poster="mymovie.jpg" width="300" height="200"> 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> VideoPlayerExample01.htm 以上基本的HTML再加上一些JavaScript就可以变成一个简单的视频播放器。以下就是 JavaScript代码。 //取得元素的引用 var player = document.getElementById("player"), btn = document.getElementById("video-btn"), curtime = document.getElementById("curtime"), duration = document.getElementById("duration"); //更新播放时间 duration.innerHTML = player.duration; //为按钮添加事件处理程序 EventUtil.addHandler(btn, "click", function(event){ if (player.paused){ player.play(); btn.value = "Pause"; } else { player.pause(); btn.value = "Play"; } }); //定时更新当前时间 setInterval(function(){ curtime.innerHTML = player.currentTime; }, 250); VideoPlayerExample01.htm 以上JavaScript 代码给按钮添加了一个事件处理程序,单击它能让视频在暂停时播放,在播放时暂停。通过<video>元素的load事件处理程序,设置了加载完视频后显示播放时间。最后,设置了一个计时器,以更新当前显示的时间。你可以进一步扩展这个视频播放器,监听更多事件,利用更多属性。而同样的代码也可以用于<audio>元素,以创建自定义的音频播放器。 16.3.4 检测编解码器的支持情况 如前所述,并非所有浏览器都支持<video>和<audio>的所有编解码器,而这基本上就意味着你必须提供多个媒体来源。不过,也有一个JavaScript API能够检测浏览器是否支持某种格式和编解码器。
这两个媒体元素都有一个 canPlayType()方法,该方法接收一种格式/编解码器字符串,返回"probably"、"maybe"或""( 空字符串)。空字符串是假值,因此可以像下面这样在 if 语句中使用canPlayType(): if (audio.canPlayType("audio/mpeg")){ //进一步处理 } 而"probably"和"maybe"都是真值,因此在if语句的条件测试中可以转换成true。 如果给canPlayType()传入了一种MIME类型,则返回值很可能是"maybe"或空字符串。这是因为媒体文件本身只不过是音频或视频的一个容器,而真正决定文件能否播放的还是编码的格式。在同时传入MIME类型和编解码器的情况下,可能性就会增加,返回的字符串会变成"probably"。下面来看几个例子。 var audio = document.getElementById("audio-player"); //很可能"maybe" if (audio.canPlayType("audio/mpeg")){ //进一步处理 } //可能是"probably" if (audio.canPlayType("audio/ogg; codecs=\"vorbis\"")){ //进一步处理 } 注意,编解码器必须用引号引起来才行。下表列出了已知的已得到支持的音频格式和编解码器。 音 频 字 符 串 支持的浏览器 AAC MP3 Vorbis WAV audio/mp4; codecs="mp4a.40.2" audio/mpeg audio/ogg; codecs="vorbis" audio/wav; codecs="1" IE9+、Safari 4+、iOS版Safari IE9+、Chrome Firefox 3.5+、Chrome、Opera 10.5+ Firefox 3.5+、Opera 10.5+、Chrome 当然,也可以使用canPlayType()来检测视频格式。下表列出了已知的已得到支持的音频格式和编解码器。 视 频 字 符 串 支持的浏览器 H.264 Theora WebM video/mp4; codecs="avc1.42E01E, mp4a.40.2" video/ogg; codecs="theora" video/webm; codecs="vp8, vorbis" IE9+、Safari 4+、iOS版Safari、Android版WebKit Firefox 3.5+、Opera 10.5、Chrome Firefox 4+、Opera 10.6、Chrome 16.3.5 Audio类型 <audio>元素还有一个原生的JavaScript构造函数Audio,可以在任何时候播放音频。从同为DOM元素的角度看,Audio与Image很相似,但Audio不用像Image那样必须插入到文档中。只要创建一
个新实例,并传入音频源文件即可。 var audio = new Audio("sound.mp3"); EventUtil.addHandler(audio, "canplaythrough", function(event){ audio.play(); }); 创建新的Audio实例即可开始下载指定的文件。下载完成后,调用play()就可以播放音频。 在iOS中,调用play()时会弹出一个对话框,得到用户的许可后才能播放声音。如果想在一段音频播放后再播放另一段音频,必须在onfinish事件处理程序中调用play()方法。 16.4 历史状态管理 历史状态管理是现代Web应用开发中的一个难点。在现代Web应用中,用户的每次操作不一定会打开一个全新的页面,因此“后退”和“前进”按钮也就失去了作用,导致用户很难在不同状态间切换。要解决这个问题,首选使用hashchange事件(第13章曾讨论过)。HTML5通过更新history对象为管理历史状态提供了方便。 通过hashchange 事件,可以知道URL的参数什么时候发生了变化,即什么时候该有所反应。而通过状态管理 API,能够在不加载新页面的情况下改变浏览器的 URL。为此,需要使用history.pushState()方法,该方法可以接收三个参数:状态对象、新状态的标题和可选的相对URL。例如: history.pushState({name:"Nicholas"}, "Nicholas' page", "nicholas.html"); 执行 pushState()方法后,新的状态信息就会被加入历史状态栈,而浏览器地址栏也会变成新的相对URL。但是,浏览器并不会真的向服务器发送请求,即使状态改变之后查询location.href也会返回与地址栏中相同的地址。另外,第二个参数目前还没有浏览器实现,因此完全可以只传入一个空字符串,或者一个短标题也可以。而第一个参数则应该尽可能提供初始化页面状态所需的各种信息。 因为 pushState()会创建新的历史状态,所以你会发现“后退”按钮也能使用了。按下“后退”按钮,会触发window对象的popstate事件①。popstate事件的事件对象有一个state属性,这个属性就包含着当初以第一个参数传递给pushState()的状态对象。 EventUtil.addHandler(window, "popstate", function(event){ var state = event.state; if (state){ //第一个页面加载时 state 为空 processState(state); } }); 得到这个状态对象后,必须把页面重置为状态对象中的数据表示的状态(因为浏览器不会自动为你做这些)。记住,浏览器加载的第一个页面没有状态,因此单击“后退”按钮返回浏览器加载的第一个页面时,event.state 值为null。 要更新当前状态,可以调用replaceState(),传入的参数与pushState()的前两个参数相同。调用这个方法不会在历史状态栈中创建新状态,只会重写当前状态。
history.replaceState({name:"Greg"}, "Greg's page"); 支持HTML5历史状态管理的浏览器有Firefox 4+、Safari 5+、Opera 11.5+和 Chrome。在 Safari和Chrome 中,传递给 pushState()或 replaceState()的状态对象中不能包含 DOM元素。而 Firefox支持在状态对象中包含DOM元素。Opera还支持一个history.state 属性,它返回当前状态的状态对象。 在使用HTML5的状态管理机制时,请确保使用pushState()创造的每一个“假” URL,在Web服务器上都有一个真的、实际存在的URL与之对应。否则,单击“刷新”按钮会导致404错误。 16.5 小结 HTML5除了定义了新的标记规则,还定义了一些JavaScript API。这些API是为了让开发人员创建出更好的、能够与桌面应用媲美的用户界面而设计的。本章讨论了如下API。 跨文档消息传递 API能够让我们在不降低同源策略安全性的前提下,在来自不同域的文档间传递消息。 原生拖放功能让我们可以方便地指定某个元素可拖动,并在操作系统要放置时做出响应。还可以创建自定义的可拖动元素及放置目标。 新的媒体元素<audio>和<video>拥有自己的与音频和视频交互的API。并非所有浏览器支持所有的媒体格式,因此应该使用canPlayType()检查浏览器是否支持特定的格式。 历史状态管理让我们不必卸载当前页面即可修改浏览器的历史状态栈。有了这种机制,用户就可以通过“后退”和“前进”按钮在页面状态间切换,而这些状态完全由JavaScript进行控制。
1.发送消息
postMessage(msg,domain)用于发送跨文档消息。第一个参数是要传递的消息内容,第二个参数表示接收方来自哪个域。第二个参数有助于提高安全性,如果发现域不匹配则不会进行操作。
2.接收消息
接收到消息时,会触发window对象的message事件。传递给onmessage事件处理程序的对象主要包含三个信息:
data:对应postMessage()第一个参数传入的内容;
origin:发送消息的文档所在域。接收到消息后,检测消息来源是非常重要的步骤。
source:发送消息的文档的window对象代理。
实例:
outer.html:
<body>
<span id="msg"></span>
<input type="text"id="input">
<input type="button"id="submit"value="提交">
<iframe width="300"height="150"src="inner.html"id="frame"></iframe>
<script type="text/javascript"src="EventUtil.js"></script>
<script type="text/javascript">
varbtn =document.getElementById('submit'); //向iframe发送消息
EventUtil.addHandler(btn, 'click', function(event) { varinput =document.getElementById('input') .value; variframe =document.getElementById('frame') .contentWindow; if(iframe.postMessage) { iframe.postMessage(input, '*'
);
}
//else{ alert('do not support postMessage'); } }); 接收iframe发送的消息
EventUtil.addHandler(window,'message',function(event){
</scriptvarmsg =document.getElementById('msg'); msg.innerHTML=event.data; }); >
</body>
inner.html:
<body> <span id="msg"></span> <input type="text" id="input"> <input type="button" id="submit" value="提交"> <script type="text/javascript" src="EventUtil.js"></script> <script type="text/javascript"> varmsg = document.getElementById('msg');
//接收外层主窗体发送的消息 EventUtil.addHandler(window, 'message', function(event){ msg.innerHTML =event.data; }); varsubmit=document.getElementById("submit");
//向外层主窗体发送消息 EventUtil.addHandler(submit,'click',function(event){ if(parent.postMessage) { varinput = document.getElementById('input').value; parent.postMessage(input, '*'
);
}
else{ alert('do not support postMessage'); } }); </script> </body>
二、拖放
1. 拖放事件
被拖动元素会依次触发以下事件:
dragstart:按下鼠标键并开始移动时触发;
drag:在元素被拖动期间会持续触发该事件;
dragend:拖动停止时触发。
当元素拖动到一个目标元素时,会依次触发以下事件:
dragenter:元素被拖动到目标元素上时触发。
dragover:被拖动的元素在目标元素范围内移动时会持续触发。
dragleave或drop:当被拖动元素离开目标元素时触发dragleave。如果元素放到了目标元素中,则触发drop而不在触发dragleave。
虽然大多数元素支持拖放的目标元素事件,但是这些元素默认是不允许放置的。所以要使用preventDefault()取消这些元素的默认事件。
<img src="http://static.cnblogs.com/images/logo_small.gif" id="img" > <div id="target" style="margin-right:10px;border:1px solid black; width:100px; height:100px;"></div> <script type='text/javascript' src="EventUtil.js"></script> <script type='text/javascript'> varimage = document.getElementById("img"); varhandlerImg=function(event){ event=EventUtil.getEvent(event); console.log(event.type); } EventUtil.addHandler(image, "dragstart", handlerImg); EventUtil.addHandler(image, "drag", handlerImg); EventUtil.addHandler(image, "dragend", handlerImg); varhandler=function(event){ event=
EventUtil.getEvent(event); event.preventDefault();
console.log(event.type); }
vartarget=document.getElementById("target"); EventUtil.addHandler(target,"dragenter",handler); EventUtil.addHandler(target,"dragover",handler); EventUtil.addHandler(target,"dragleave",handler); EventUtil.addHandler(target,"drop",handler);
</script>
2.dataTransfer对象
dataTransfer对象是事件对象的一个属性,用于从被拖动的元素向目标元素传递字符串类型的数据。保存在dataTransfer对象中的数据只能在ondrop事件中读取。几个常用方法:
setData(type,value):设置用来设置数据。第一个参数是“Text”或者“url”,第二个参数是字符串类型的数据。text和url类型还是略有区别的,如果将数据保存为url,那么浏览器会把它当作网页中的链接。如果将其拖放到浏览器窗口中,浏览器就会打开该链接。
getData(type):参数type是"Text"或“url”,与set中的设置保持一致。
setDragImage(target,x,y):指定一幅图像,当拖动发生时,显示在光标下方。
<img src="http://static.cnblogs.com/images/logo_small.gif"id="img">
<div id="target"style="float:left;margin-left:300px;border:1px solid black; width:200px; height:100px;"></div>
<script type='text/javascript' src="EventUtil.js"></script>
<script type='text/javascript'>
varimage =document.getElementById('img');
varhandlerImg =function(event) { event =EventUtil.getEvent(event); vartarget =EventUtil.getTarget(event); event.dataTransfer.setData('Text', target.id); event.dataTransfer.setData('url',target.getAttribute('src'
));
event.dataTransfer.setDragImage(target,
'dragstart'10,10); } EventUtil.addHandler(image, , handlerImg);
varhandler =function(event) { event =EventUtil.getEvent(event); vartarget =EventUtil.getTarget(event); switch(event.type) { case'drop': event.preventDefault(); varid =event.dataTransfer.getData('Text'); varurl=event.dataTransfer.getData('url'
);
target.appendChild(document.getElementById(id)); target.innerHTML
break+=url; ; case'dragover': event.preventDefault(); break; } }
vartarget =document.getElementById('target'); EventUtil.addHandler(target, 'dragover', handler); EventUtil.addHandler(target, 'drop', handler);
</script>
3.可拖放
默认情况下,图像、文本等是可以拖动的,大多数其他元素不能拖动。如果想让某个元素可拖动,只需设置draggable=true即可。
<div id="target"style="border:1px solid black; width:200px; height:100px;"draggable="false"></div>
三、媒体元素
1.音频和视频控件
<audio id="player"src="billyBrowsers.ogg"poster="smile.gif"controls>不支持音频</audio>
<video id="player"src="billyBrowsers.ogg"poster="mymovie.jpg"width="300"height="200">不支持视频</video>
2.常用属性
autoplay:布尔型,设置是否自动播放。
controls:布尔型,用来设置浏览器中的自带控件是否显示。
currentSrc:当前播放的媒体文件url。
duration:媒体的总播放时间。
currentTime:当前已经播放的秒数。
loop:布尔型,媒体是否自动循环播放。
plaused:媒体播放器是否暂停。
src:媒体文件的来源。
<div class="mediaplayer">
<div class="video">
<audio id="player"src="billyBrowsers.ogg"poster="smile.gif"controls autoplay='true'>不支持音频</audio>
</div>
<div class="controls">
<input type="button"value="Play"id="audio-btn"/>
<span id="curtime">0</span>/<span id="duration">0</span>
</div>
</div>
<script>
window.onload =function(){ varplayer =document.getElementById("player"); varbtn =document.getElementById("audio-btn"); varcurtime =document.getElementById("curtime"); varduration =document.getElementById("duration"); duration.innerHTML =player.duration; EventUtil.addHandler(btn, "click", function(event){ if
(player.paused){ console.log(player.currentSrc); player.play();
btn.value
else="Pause"; }
{ player.pause();
btn.value
="Play"; } }); setInterval(function(){ curtime.innerHTML =
player.currentTime;
},
</250); }; script>
3.Audio类型
<audio>元素有对应的构造函数Audio。其实不必将Audio插入到文档中,在创建Audio实例后就开始下载指定文件,下载完成后,调用play()方法就可以开始播放。
<script>
window.onload =function(){ varplayer=newAudio("billyBrowsers.ogg"); EventUtil.addHandler(player,"canplay",function
(){ player.play();
}) };
</script>
16.1 跨文档消息传递 跨文档消息传送(cross-document messaging),有时候简称为XDM,指的是在来自不同域的页面间传递消息。例如,www.wrox.com域中的页面与位于一个内嵌框架中的p2p.wrox.com域中的页面通信。在 XDM机制出现之前,要稳妥地实现这种通信需要花很多工夫。XDM把这种机制规范化,让我们能既稳妥又简单地实现跨文档通信。 XDM的核心是postMessage()方法。在HTML5规范中,除了XDM部分之外的其他部分也会提到这个方法名,但都是为了同一个目的:向另一个地方传递数据。对于XDM而言,“另一个地方”指的是包含在当前页面中的<iframe>元素,或者由当前页面弹出的窗口。 postMessage()方法接收两个参数:一条消息和一个表示消息接收方来自哪个域的字符串。第二个参数对保障安全通信非常重要,可以防止浏览器把消息发送到不安全的地方。来看下面的例子。 //注意:所有支持XDM的浏览器也支持iframe的contentWindow属性 var iframeWindow = document.getElementById("myframe").contentWindow; iframeWindow.postMessage("A secret", "http://www.wrox.com"); 最后一行代码尝试向内嵌框架中发送一条消息,并指定框架中的文档必须来源于"http:// www.wrox.com"域。如果来源匹配,消息会传递到内嵌框架中;否则,postMessage()什么也不做。这一限制可以避免窗口中的位置在你不知情的情况下发生改变。如果传给postMessage()的第二个参数是"*",则表示可以把消息发送给来自任何域的文档,但我们不推荐这样做。 接收到XDM消息时,会触发window对象的message事件。这个事件是以异步形式触发的,因此从发送消息到接收消息(触发接收窗口的message事件)可能要经过一段时间的延迟。触发message事件后,传递给onmessage处理程序的事件对象包含以下三方面的重要信息。 data:作为postMessage()第一个参数传入的字符串数据。 origin:发送消息的文档所在的域,例如"http://www.wrox
source:发送消息的文档的window对象的代理。这个代理对象主要用于在发送上一条消息的窗口中调用 postMessage()方法。如果发送消息的窗口来自同一个域,那这个对象就是window。 接收到消息后验证发送窗口的来源是至关重要的。就像给postMessage()方法指定第二个参数,以确保浏览器不会把消息发送给未知页面一样,在onmessage 处理程序中检测消息来源可以确保传入的消息来自已知的页面。基本的检测模式如下。 EventUtil.addHandler(window, "message", function(event){ //确保发送消息的域是已知的域 if (event.origin == "http://www.wrox.com"){ //处理接收到的数据 processMessage(event.data); //可选:向来源窗口发送回执 event.source.postMessage("Received!", "http://p2p.wrox.com"); } }); 还是要提醒大家,event.source大多数情况下只是window对象的代理,并非实际的window对象。换句话说,不能通过这个代理对象访问window对象的其他任何信息。记住,只通过这个代理调用postMessage()就好,这个方法永远存在,永远可以调用。 XDM还有一些怪异之处。首先,postMessage()的第一个参数最早是作为“永远都是字符串”来实现的。但后来这个参数的定义改了,改成允许传入任何数据结构。可是,并非所有浏览器都实现了这一变化。为保险起见,使用postMessage()时,最好还是只传字符串。如果你想传入结构化的数据,最佳选择是先在要传入的数据上调用JSON.stringify(),通过 postMessage()传入得到的字符串,然后再在onmessage 事件处理程序中调用JSON.parse()。 在通过内嵌框架加载其他域的内容时,使用XDM是非常方便的。因此,在混搭(mashup)和社交网络应用中,这种传递消息的方法极为常用。有了XDM,包含<iframe>的页面可以确保自身不受恶意内容的侵扰,因为它只通过XDM与嵌入的框架通信。而XDM也可以在来自相同域的页面间使用。 支持XDM的浏览器有IE8+、Firefox 3.5+、Safari 4+、Opera、Chrome、iOS版 Safari及 Android版WebKit 。XDM 已经作为一个规范独立出来,现在它的名字叫 Web Messaging,官方页面是http://dev.w3.org/html5/postmsg/。 16.2 原生拖放 最早在网页中引入 JavaScript 拖放功能的是 IE4。当时,网页中只有两种对象可以拖放:图像和某些文本。拖动图像时,把鼠标放在图像上,按住鼠标不放就可以拖动它。拖动文本时,要先选中文本,然后可以像拖动图像一样拖动被选中的文本。在IE 4中,唯一有效的放置目标是文本框。到了IE5,拖放功能得到扩展,添加了新的事件,而且几乎网页中的任何元素都可以作为放置目标。IE5.5更进一步,让网页中的任何元素都可以拖放。(IE6同样也支持这些功能。)HTML5以IE的实例为基础制定了拖放规范。Firefox 3.5、Safari 3+和 Chrome也根据 HTML5规范实现了原生拖放功能。 说到拖放,最有意思的恐怕就是能够在框架间、窗口间,甚至在应用间拖放网页元素了。浏览器对拖放的支持为实现这些功能提供了便利
16.2.1 拖放事件 通过拖放事件,可以控制拖放相关的各个方面。其中最关键的地方在于确定哪里发生了拖放事件,有些事件是在被拖动的元素上触发的,而有些事件是在放置目标上触发的。拖动某元素时,将依次触发下列事件: (1) dragstart (2) drag (3) dragend 按下鼠标键并开始移动鼠标时,会在被拖放的元素上触发dragstart事件。此时光标变成“不能放”符号(圆环中有一条反斜线),表示不能把元素放到自己上面。拖动开始时,可以通过 ondragstart事件处理程序来运行JavaScript代码。 触发dragstart 事件后,随即会触发drag 事件,而且在元素被拖动期间会持续触发该事件。这个事件与mousemove事件相似,在鼠标移动过程中,mousemove事件也会持续发生。当拖动停止时(无论是把元素放到了有效的放置目标,还是放到了无效的放置目标上),会触发dragend事件。 上述三个事件的目标都是被拖动的元素。默认情况下,浏览器不会在拖动期间改变被拖动元素的外观,但你可以自己修改。不过,大多数浏览器会为正被拖动的元素创建一个半透明的副本,这个副本始终跟随着光标移动。 当某个元素被拖动到一个有效的放置目标上时,下列事件会依次发生: (1) dragenter (2) dragover (3) dragleave 或 drop 只要有元素被拖动到放置目标上,就会触发dragenter事件(类似于mouseover事件)。紧随其后的是dragover 事件,而且在被拖动的元素还在放置目标的范围内移动时,就会持续触发该事件。如果元素被拖出了放置目标,dragover 事件不再发生,但会触发dragleave 事件(类似于 mouseout事件)。如果元素被放到了放置目标中,则会触发drop事件而不是dragleave事件。上述三个事件的目标都是作为放置目标的元素。 16.2.2 自定义放置目标 在拖动元素经过某些无效放置目标时,可以看到一种特殊的光标(圆环中有一条反斜线),表示不能放置。虽然所有元素都支持放置目标事件,但这些元素默认是不允许放置的。如果拖动元素经过不允许放置的元素,无论用户如何操作,都不会发生drop事件。不过,你可以把任何元素变成有效的放置目标,方法是重写dragenter和dragover事件的默认行为。例如,假设有一个ID为"droptarget"的<div>元素,可以用如下代码将它变成一个放置目标。 var droptarget = document.getElementById("droptarget"); EventUtil.addHandler(droptarget, "dragover", function(event){ EventUtil.preventDefault(event); }); EventUtil.addHandler(droptarget, "dragenter", function(event){ EventUtil.preventDefault(event); });
以上代码执行后,你就会发现当拖动着元素移动到放置目标上时,光标变成了允许放置的符号。当然,释放鼠标也会触发drop事件。 在Firefox 3.5+中,放置事件的默认行为是打开被放到放置目标上的URL。换句话说,如果是把图像拖放到放置目标上,页面就会转向图像文件;而如果是把文本拖放到放置目标上,则会导致无效URL错误。因此,为了让Firefox支持正常的拖放,还要取消drop事件的默认行为,阻止它打开URL: EventUtil.addHandler(droptarget, "drop", function(event){ EventUtil.preventDefault(event); }); 16.2.3 dataTransfer对象 只有简单的拖放而没有数据变化是没有什么用的。为了在拖放操作时实现数据交换,IE 5 引入了dataTransfer 对象,它是事件对象的一个属性,用于从被拖动元素向放置目标传递字符串格式的数据。因为它是事件对象的属性,所以只能在拖放事件的事件处理程序中访问 dataTransfer 对象。在事件处理程序中,可以使用这个对象的属性和方法来完善拖放功能。目前,HTML5 规范草案也收入了dataTransfer 对象。 dataTransfer 对象有两个主要方法:getData()和 setData()。不难想象,getData()可以取得由setData()保存的值。setData()方法的第一个参数,也是getData()方法唯一的一个参数,是一个字符串,表示保存的数据类型,取值为"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/uri-list"。 实际上,dataTransfer 对象可以为每种 MIME 类型都保存一个值。换句话说,同时在这个对象中保存一段文本和一个URL不会有任何问题。不过,保存在dataTransfer对象中的数据只能在drop事件处理程序中读取。如果在ondrop 处理程序中没有读到数据,那就是dataTransfer 对象已经被销毁,数据也丢失了。 在拖动文本框中的文本时,浏览器会调用 setData()方法,将拖动的文本以"text"格式保存在dataTransfer 对象中。类似地,在拖放链接或图像时,会调用setData()方法并保存URL。然后,在这些元素被拖放到放置目标时,就可以通过 getData()读到这些数据。当然,作为开发人员,你也可以在dragstart 事件处理程序中调用setData(),手工保存自己要传输的数据,以便将来使用。 将数据保存为文本和保存为 URL是有区别的。如果将数据保存为文本格式,那么数据不会得到任何特殊处理。而如果将数据保存为URL,浏览器会将其当成网页中的链接。换句话说,如果你把它放置到另一个浏览器窗口中,浏览器就会打开该URL。 Firefox 在其第 5 个版本之前不能正确地将"url"和"text"映射为"text/uri-list"和"text/plain"。但是却能把"Text"(T大写)映射为"text/plain"。
下从 dataTransfer 对象取得数据,最好在取得 URL 数据时检测两个值,而在取得文本数据时使用"Text"。 var dataTransfer = event.dataTransfer; //读取URL var url = dataTransfer.getData("url") ||dataTransfer.getData("text/uri-list"); //读取文本 var text = dataTransfer.getData("Text"); DataTransferExample01.htm 注意,一定要把短数据类型放在前面,因为IE 10及之前的版本仍然不支持扩展的MIME类型名,而它们在遇到无法识别的数据类型时,会抛出错误。 16.2.4 dropEffect与effectAllowed 利用 dataTransfer 对象,可不光是能够传输数据,还能通过它来确定被拖动的元素以及作为放置目标的元素能够接收什么操作。为此,需要访问dataTransfer 对象的两个属性:dropEffect 和effectAllowed。 其中,通过 dropEffect 属性可以知道被拖动的元素能够执行哪种放置行为。这个属性有下列 4个可能的值。 "none":不能把拖动的元素放在这里。这是除文本框之外所有元素的默认值。 "move":应该把拖动的元素移动到放置目标。 "copy":应该把拖动的元素复制到放置目标。 "link":表示放置目标会打开拖动的元素(但拖动的元素必须是一个链接,有URL)。 在把元素拖动到放置目标上时,以上每一个值都会导致光标显示为不同的符号。然而,要怎样实现光标所指示的动作完全取决于你。换句话说,如果你不介入,没有什么会自动地移动、复制,也不会打开链接。总之,浏览器只能帮你改变光标的样式,而其他的都要靠你自己来实现。要使用dropEffect属性,必须在ondragenter事件处理程序中针对放置目标来设置它。 dropEffect 属性只有搭配 effectAllowed 属性才有用。effectAllowed 属性表示允许拖动元素的哪种dropEffect,effectAllowed 属性可能的值如下。 "uninitialized":没有给被拖动的元素设置任何放置行为。 "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属性。
假设你想允许用户把文本框中的文本拖放到一个<div>元素中。首先,必须将 dropEffect 和effectAllowed 设置为"move"。但是,由于<div>元素的放置事件的默认行为是什么也不做,所以文本不可能自动移动。重写这个默认行为,就能从文本框中移走文本。然后你就可以自己编写代码将文本插入到<div>中,这样整个拖放操作就完成了。如果你将dropEffect和effectAllowed的值设置为"copy",那就不会自动移走文本框中的文本。 Firefox 5 及之前的版本在处理effectAllowed 属性时有一个问题,即如果你在代码中设置了这个属性的值,那不一定会触发drop事件。 16.2.5 可拖动 默认情况下,图像、链接和文本是可以拖动的,也就是说,不用额外编写代码,用户就可以拖动它们。文本只有在被选中的情况下才能拖动,而图像和链接在任何时候都可以拖动。 让其他元素可以拖动也是可能的。HTML5为所有HTML元素规定了一个draggable属性,表示元素是否可以拖动。图像和链接的draggable属性自动被设置成了true,而其他元素这个属性的默认值都是false。要想让其他元素可拖动,或者让图像或链接不能拖动,都可以设置这个属性。例如: <!-- 让这个图像不可以拖动 --> <img src="smile.gif" draggable="false" alt="Smiley face"> <!-- 让这个元素可以拖动 --> <div draggable="true">...</div> 支持draggable 属性的浏览器有IE 10+、Firefox 4+、Safari 5+和 Chrome。Opera 11.5及之前的版本都不支持HTML5的拖放功能。另外,为了让Firefox支持可拖动属性,还必须添加一个ondragstart事件处理程序,并在dataTransfer对象中保存一些信息。 在IE9及更早版本中,通过mousedown 事件处理程序调用dragDrop()能够让任何元素可拖动。而在 Safari 4 及之前版本中,必须额外给相应元素设置 CSS 样式–khtml-user-drag: element。 16.2.6 其他成员 HTML5规范规定dataTransfer 对象还应该包含下列方法和属性。 addElement(element):为拖动操作添加一个元素。添加这个元素只影响数据(即增加作为拖动源而响应回调的对象),不会影响拖动操作时页面元素的外观。在写作本书时,只有Firefox 3.5+实现了这个方法。 clearData(format):清除以特定格式保存的数据。实现这个方法的浏览器有IE、Fireforx 3.5+、Chrome和 Safari 4+。 setDragImage(element, x, y):指定一幅图像,当拖动发生时,显示在光标下方。这个方
法接收的三个参数分别是要显示的HTML元素和光标在图像中的x、y坐标。其中,HTML元素可以是一幅图像,也可以是其他元素。是图像则显示图像,是其他元素则显示渲染后的元素。实现这个方法的浏览器有Firefox 3.5+、Safari 4+和Chrome。 types:当前保存的数据类型。这是一个类似数组的集合,以"text"这样的字符串形式保存着数据类型。实现这个属性的浏览器有IE10+、Firefox 3.5+和Chrome。 16.3 媒体元素 随着音频和视频在 Web 上的迅速流行,大多数提供富媒体内容的站点为了保证跨浏览器兼容性,不得不选择使用Flash。HTML5新增了两个与媒体相关的标签,让开发人员不必依赖任何插件就能在网页中嵌入跨浏览器的音频和视频内容。这两个标签就是<audio>和<video>。 这两个标签除了能让开发人员方便地嵌入媒体文件之外,都提供了用于实现常用功能的 JavaScript API,允许为媒体创建自定义的控件。这两个元素的用法如下。 <!-- 嵌入视频 --> <video src="conference.mpg" id="myVideo">Video player not available.</video> <!-- 嵌入音频 --> <audio src="song.mp3" id="myAudio">Audio player not available.</audio> 使用这两个元素时,至少要在标签中包含src 属性,指向要加载的媒体文件。还可以设置width和height 属性以指定视频播放器的大小,而为poster属性指定图像的URI可以在加载视频内容期间显示一幅图像。另外,如果标签中有controls 属性,则意味着浏览器应该显示UI控件,以便用户直接操作媒体。位于开始和结束标签之间的任何内容都将作为后备内容,在浏览器不支持这两个媒体元素的情况下显示。 因为并非所有浏览器都支持所有媒体格式,所以可以指定多个不同的媒体来源。为此,不用在标签中指定src属性,而是要像下面这样使用一或多个<source>元素。 <!-- 嵌入视频 --> <video id="myVideo"> <source src="conference.webm" type="video/webm; codecs='vp8, vorbis'"> <source src="conference.ogv" type="video/ogg; codecs='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> 关于视频和音频编解码器的内容超出了本书讨论的范围。作者在此只想告诉大家,不同的浏览器支持不同的编解码器,因此一般来说指定多种格式的媒体来源是必需的。支持这两个媒体元素的浏览器有IE9+、Firefox 3.5+、Safari 4+、Opera 10.5+、Chrome、iOS版 Safari和 Android版 WebKit。
16.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 浮点数 取得或设置媒体文件中开始播放的位置,以秒表示 totalBytes 整数 当前资源所需的总字节数 videoHeight 整数 返回视频(不一定是元素)的高度。只适用于<video> videoWidth 整数 返回视频(不一定是元素)的宽度。只适用于<video> volume 浮点数 取得或设置当前音量,值为0.0到1.0 其中很多属性也可以直接在<audio>和<video>元素中设置。
16.3.2 事件 除了大量属性之外,这两个媒体元素还可以触发很多事件。这些事件监控着不同的属性的变化,这些变化可能是媒体播放的结果,也可能是用户操作播放器的结果。下表列出了媒体元素相关的事件。 事 件 触发时机 abort 下载中断 canplay 可以播放时;readyState值为2 canplaythrough 播放可继续,而且应该不会中断;readyState值为3 canshowcurrentframe 当前帧已经下载完成;readyState值为1 dataunavailable 因为没有数据而不能播放;readyState值为0 durationchange duration属性的值改变 emptied 网络连接关闭 empty 发生错误阻止了媒体下载 ended 媒体已播放到末尾,播放停止 error 下载期间发生网络错误 load 所有媒体已加载完成。这个事件可能会被废弃,建议使用canplaythrough loadeddata 媒体的第一帧已加载完成 loadedmetadata 媒体的元数据已加载完成 loadstart 下载已开始 pause 播放已暂停 play 媒体已接收到指令开始播放 playing 媒体已实际开始播放 progress 正在下载 ratechange 播放媒体的速度改变 seeked 搜索结束 seeking 正移动到新位置 stalled 浏览器尝试下载,但未接收到数据 timeupdate currentTime被以不合理或意外的方式更新 volumechange volume属性值或muted属性值已改变 waiting 播放暂停,等待下载更多数据 这些事件之所以如此具体,就是为了让开发人员只使用少量HTML和JavaScript(与创建Flash影片相比)即可编写出自定义的音频/视频播放器。 16.3.3 自定义媒体播放器 使用<audio>和<video>元素的play()和pause()方法,可以手工控制媒体文件的播放。组合使用属性、事件和这两个方法,很容易创建一个自定义的媒体播放器,如下面的例子所示。
<div class="mediaplayer"> <div class="video"> <video id="player" src="movie.mov" poster="mymovie.jpg" width="300" height="200"> 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> VideoPlayerExample01.htm 以上基本的HTML再加上一些JavaScript就可以变成一个简单的视频播放器。以下就是 JavaScript代码。 //取得元素的引用 var player = document.getElementById("player"), btn = document.getElementById("video-btn"), curtime = document.getElementById("curtime"), duration = document.getElementById("duration"); //更新播放时间 duration.innerHTML = player.duration; //为按钮添加事件处理程序 EventUtil.addHandler(btn, "click", function(event){ if (player.paused){ player.play(); btn.value = "Pause"; } else { player.pause(); btn.value = "Play"; } }); //定时更新当前时间 setInterval(function(){ curtime.innerHTML = player.currentTime; }, 250); VideoPlayerExample01.htm 以上JavaScript 代码给按钮添加了一个事件处理程序,单击它能让视频在暂停时播放,在播放时暂停。通过<video>元素的load事件处理程序,设置了加载完视频后显示播放时间。最后,设置了一个计时器,以更新当前显示的时间。你可以进一步扩展这个视频播放器,监听更多事件,利用更多属性。而同样的代码也可以用于<audio>元素,以创建自定义的音频播放器。 16.3.4 检测编解码器的支持情况 如前所述,并非所有浏览器都支持<video>和<audio>的所有编解码器,而这基本上就意味着你必须提供多个媒体来源。不过,也有一个JavaScript API能够检测浏览器是否支持某种格式和编解码器。
这两个媒体元素都有一个 canPlayType()方法,该方法接收一种格式/编解码器字符串,返回"probably"、"maybe"或""( 空字符串)。空字符串是假值,因此可以像下面这样在 if 语句中使用canPlayType(): if (audio.canPlayType("audio/mpeg")){ //进一步处理 } 而"probably"和"maybe"都是真值,因此在if语句的条件测试中可以转换成true。 如果给canPlayType()传入了一种MIME类型,则返回值很可能是"maybe"或空字符串。这是因为媒体文件本身只不过是音频或视频的一个容器,而真正决定文件能否播放的还是编码的格式。在同时传入MIME类型和编解码器的情况下,可能性就会增加,返回的字符串会变成"probably"。下面来看几个例子。 var audio = document.getElementById("audio-player"); //很可能"maybe" if (audio.canPlayType("audio/mpeg")){ //进一步处理 } //可能是"probably" if (audio.canPlayType("audio/ogg; codecs=\"vorbis\"")){ //进一步处理 } 注意,编解码器必须用引号引起来才行。下表列出了已知的已得到支持的音频格式和编解码器。 音 频 字 符 串 支持的浏览器 AAC MP3 Vorbis WAV audio/mp4; codecs="mp4a.40.2" audio/mpeg audio/ogg; codecs="vorbis" audio/wav; codecs="1" IE9+、Safari 4+、iOS版Safari IE9+、Chrome Firefox 3.5+、Chrome、Opera 10.5+ Firefox 3.5+、Opera 10.5+、Chrome 当然,也可以使用canPlayType()来检测视频格式。下表列出了已知的已得到支持的音频格式和编解码器。 视 频 字 符 串 支持的浏览器 H.264 Theora WebM video/mp4; codecs="avc1.42E01E, mp4a.40.2" video/ogg; codecs="theora" video/webm; codecs="vp8, vorbis" IE9+、Safari 4+、iOS版Safari、Android版WebKit Firefox 3.5+、Opera 10.5、Chrome Firefox 4+、Opera 10.6、Chrome 16.3.5 Audio类型 <audio>元素还有一个原生的JavaScript构造函数Audio,可以在任何时候播放音频。从同为DOM元素的角度看,Audio与Image很相似,但Audio不用像Image那样必须插入到文档中。只要创建一
个新实例,并传入音频源文件即可。 var audio = new Audio("sound.mp3"); EventUtil.addHandler(audio, "canplaythrough", function(event){ audio.play(); }); 创建新的Audio实例即可开始下载指定的文件。下载完成后,调用play()就可以播放音频。 在iOS中,调用play()时会弹出一个对话框,得到用户的许可后才能播放声音。如果想在一段音频播放后再播放另一段音频,必须在onfinish事件处理程序中调用play()方法。 16.4 历史状态管理 历史状态管理是现代Web应用开发中的一个难点。在现代Web应用中,用户的每次操作不一定会打开一个全新的页面,因此“后退”和“前进”按钮也就失去了作用,导致用户很难在不同状态间切换。要解决这个问题,首选使用hashchange事件(第13章曾讨论过)。HTML5通过更新history对象为管理历史状态提供了方便。 通过hashchange 事件,可以知道URL的参数什么时候发生了变化,即什么时候该有所反应。而通过状态管理 API,能够在不加载新页面的情况下改变浏览器的 URL。为此,需要使用history.pushState()方法,该方法可以接收三个参数:状态对象、新状态的标题和可选的相对URL。例如: history.pushState({name:"Nicholas"}, "Nicholas' page", "nicholas.html"); 执行 pushState()方法后,新的状态信息就会被加入历史状态栈,而浏览器地址栏也会变成新的相对URL。但是,浏览器并不会真的向服务器发送请求,即使状态改变之后查询location.href也会返回与地址栏中相同的地址。另外,第二个参数目前还没有浏览器实现,因此完全可以只传入一个空字符串,或者一个短标题也可以。而第一个参数则应该尽可能提供初始化页面状态所需的各种信息。 因为 pushState()会创建新的历史状态,所以你会发现“后退”按钮也能使用了。按下“后退”按钮,会触发window对象的popstate事件①。popstate事件的事件对象有一个state属性,这个属性就包含着当初以第一个参数传递给pushState()的状态对象。 EventUtil.addHandler(window, "popstate", function(event){ var state = event.state; if (state){ //第一个页面加载时 state 为空 processState(state); } }); 得到这个状态对象后,必须把页面重置为状态对象中的数据表示的状态(因为浏览器不会自动为你做这些)。记住,浏览器加载的第一个页面没有状态,因此单击“后退”按钮返回浏览器加载的第一个页面时,event.state 值为null。 要更新当前状态,可以调用replaceState(),传入的参数与pushState()的前两个参数相同。调用这个方法不会在历史状态栈中创建新状态,只会重写当前状态。
history.replaceState({name:"Greg"}, "Greg's page"); 支持HTML5历史状态管理的浏览器有Firefox 4+、Safari 5+、Opera 11.5+和 Chrome。在 Safari和Chrome 中,传递给 pushState()或 replaceState()的状态对象中不能包含 DOM元素。而 Firefox支持在状态对象中包含DOM元素。Opera还支持一个history.state 属性,它返回当前状态的状态对象。 在使用HTML5的状态管理机制时,请确保使用pushState()创造的每一个“假” URL,在Web服务器上都有一个真的、实际存在的URL与之对应。否则,单击“刷新”按钮会导致404错误。 16.5 小结 HTML5除了定义了新的标记规则,还定义了一些JavaScript API。这些API是为了让开发人员创建出更好的、能够与桌面应用媲美的用户界面而设计的。本章讨论了如下API。 跨文档消息传递 API能够让我们在不降低同源策略安全性的前提下,在来自不同域的文档间传递消息。 原生拖放功能让我们可以方便地指定某个元素可拖动,并在操作系统要放置时做出响应。还可以创建自定义的可拖动元素及放置目标。 新的媒体元素<audio>和<video>拥有自己的与音频和视频交互的API。并非所有浏览器支持所有的媒体格式,因此应该使用canPlayType()检查浏览器是否支持特定的格式。 历史状态管理让我们不必卸载当前页面即可修改浏览器的历史状态栈。有了这种机制,用户就可以通过“后退”和“前进”按钮在页面状态间切换,而这些状态完全由JavaScript进行控制。