JS学习15(HTML5脚本编程)

HTML5不仅定义了新的HTML标记,也定义了很多新的JS API。

跨文档消息传送(XDM)

指的是在来自不同域的页面间传递消息。例如,www.wrox.com域中的页面与位于一个内嵌框架中的p2p.wrox.com域中的页面通信。
XDM的核心是postMessage()方法。对于XDM而言,就是将信息发送至包含在当前页面中的iframe元素,或者由当前页面弹出的窗口。
这个方法接受两个参数,一个消息字符串和一个表示消息接受方来自哪个域的字符串。如果内嵌框架的来源不符合参数,则什么也不做,传递“*”代表匹配所有文档。

var iframeWindow = document.getElementById("myframe").contentWindow; 
iframeWindow.postMessage("A secret", "http://www.wrox.com");

接受消息时会触发window对象的message事件,这个事件是以异步形式触发的。其事件处理程序包含如下信息:

  • data:postMessage()的字符串数据
  • origin:发送消息的文档所在的域
  • source:发送消息的文档的window对象的代理,这个代理对象主要用于在发送上一条的窗口中调用postMessage()方法来回复,如果发送消息的窗口来自同一个域,那这个对象就是window。
EventUtil.addHandler(window, "message", function(event){
    alert(event.origin);
    alert(event.data);
    alert(event.source.parentVar);
    event.source.postMessage("Received!", "http://p2p.wrox.com");
});

source大多数情况下只是window对象的代理,并非实际的window对象,除了发送返回消息,不建议访问这个对象的其它属性。
发送的数据后来被改成了可以是任何数据结构,但是并非所有浏览器都实现了,建议还是只传字符串,结构化数据使用JSON.stringify()和JSON.parse()。

原生拖放

在框架间,窗口间,甚至应用间拖放网页元素。

拖放事件

通过拖放事件,可以控制拖放相关的各个方面,其中最关键的地方在于确定那里发生了拖放事件,有些事件是在被拖动的元素上产生的,有些是在放置目标上触发的。
拖动某元素时将依次在被拖动的元素上触发下列事件:

  1. dragstart:按下鼠标且鼠标移动开始时
  2. drag:在元素被拖动的过程中持续触发
  3. dragend:当拖动停止时,无论把元素放到有效的放置目标上还是无效的上,都会触发dragend事件。

在拖动的过程中,被拖动的元素的外观是可以改变的,一般浏览器会默认创建一个半透明的副本,这个副本始终跟随光标移动。
当某个元素被放置到有效的放置目标的时候,下列事件会在放置目标会依次发生:

  1. dragenter:元素被拖动到放置目标上,就回触发一次dragenter
  2. dragover:元素在放置目标上继续被拖动时,会持续触发这个事件
  3. dragleave或drop:如果元素又被拖离了目标范围,触发dragleave,dragover事件不再触发,如果直接放置在了目标中,会触发drop事件。

自定义放置目标

所有元素都支持放置目标事件,但有些元素是默认不允许放置的,如果拖动元素经过不允许放置的的元素,是不会触发drop事件的,重写其dragenter和dragover事件可以使其变为可放置的:

EventUtil.addHandler(dropDiv, "dragover", function(event){
    EventUtil.preventDefault(event);
});
EventUtil.addHandler(dropDiv, "dragenter", function(event){
    EventUtil.preventDefault(event);
});
EventUtil.addHandler(dropDiv, "drop", function(event){
    alert("droped");
});

dataTransfer对象

为了在拖放操作时实现数据的交换,引入了dataTransfer对象,这是事件对象的一个属性,这个对象有两个方法:getData()和 setData()。这两个方法都需要一个代表数据类型的参数,在HTML5中,这个是MIME类型,为了向后兼容,还可以是"text"和"URL",只不过他们其实是被映射为MIME类型"text/plain"和"text/uri-list"。setData还需要一个对应的数据作为参数。在这个对象中,对不同的MIME可以同时存储多个值。
这个对象只在drop事件中读取。
setData在拖动开始时设置,拖动文本,链接或图像时,浏览器会自动调用这个方法保存文字或URL。我们自己也可以在dragstart事件中调用来保存我们自己的数据。
getData在drop事件中调用,获取相关数据。

event.dataTransfer.setData("URL", "http://www.wrox.com/");
var url = dataTransfer.getData("url")||dataTransfer.getData("text/uri-list");

dropEffect与effectAllowed

利用dataTransfer对象,不光能传送数据,还能确定被拖动元素以及作为放置目标的元素能够接收什么操作。
dropEffect属性可以知道被拖动元素能执行哪种放置行为:

  • none
  • move
  • copy
  • link

这个属性要在开始拖动ondragstart时设置
effectAllowed表示允许拖动元素的哪种dropEffect。

  • uninitialized
  • none
  • copy
  • link
  • move
  • copyLink
  • copyMove
  • linkMove
  • all

这个也要在ondragstart时设置

可拖动

<div draggable="true">...</div>

支持这个属性的有IE 10+ Firefox 4+ Safari 5+ Chrome

其它成员

addElement(element)
clearData(format)
setDragImage(element, x, y)
types

媒体元素

新增标签audio、video标签。

<video src="conference.mpg" id="myVideo">Video player not available.</video>

如果为了编码格式的兼容有多个源:

<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>

属性

这两个元素提供了完善的JS接口:
autoplay
buffered
bufferedBytes
bufferingRate
bufferingThrottled
controls
currentLoop
currentSrc
currentTime
defaultPlaybackRate
duration
ended
loop
muted
networkState
paused
playbackRate
played
readyState
seekable
seeking
src
start
totalBytes
videoHeight
videoWidth
volume

事件

abort
canplay
canplaythrough
canshowcurrentframe
dataunavailable
durationchange
emptied
empty
ended
error
load
loadeddata
loadedmetadata
loadstart
pause
play
playing
progress
ratechange
seeked
seeking
stalled
timeupdate
volumechange
waiting

自定义媒体播放器

由于有如此全面的API,使得自定义媒体播放器成为可行。

<div class="mediaplayer">
    <div class="video">
        <video id="player" src="movie.mov" poster="img/paypal2.png"
               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>
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);

检测编解码器的支持情况

canPlayType()这个方法用来检测,它接收格式,编码字符串返回"probably"、"maybe"、""。

//只传入格式,并不知道编码的情况下,真返回的是maybe,假返回“”
if (audio.canPlayType("audio/mpeg")){
//      
}
//都传入时可能性增加了,真就返回"probably"
if (audio.canPlayType("audio/ogg; codecs=\"vorbis\"")){
//      
}

Audio类型

Audio还有一个原生的JS构造函数,不需要将其加入文档中,直接就可以播放:

var audio = new Audio("sound.mp3"); EventUtil.addHandler(audio, "canplaythrough", function(event){
    audio.play();
});

历史状态管理

在现在的Web应用中,用户的每次操作并不一定会打开一个新的页面,前进和后退按钮在这里也就失去了作用。HTML5通过更新history对象为管理历史状态提供了方便。
hashchange事件可以知道URL的参数什么时候发生了变化,这时使用history.pushState()可以在不加载新页面的情况下改变浏览器的URL,该方法接收3个参数:状态对象,新状态的标题和可选的相对URL。

history.pushState({name:"Nicholas"}, "Nicholas' page", "nicholas.html");

这个方法执行后,新的状态会被加入历史状态栈,浏览器的地址栏也会变成新的相对URL。但其实并没有像服务器发送数据。其中的第一个参数是用来初始化这个页面的数据用的。
这时后退按钮就能使用了,这时点击后退按钮会触发window对象的popstate事件,这个事件有个属性叫state,就是pushState的第一个参数。回到上一个页面,读取上一个页面的state来初始化上一个页面。比如你回到的是nicholas.html,那你读到的state的name就是Nicholas。
浏览器加载的第一个页面木有状态,其state是null。
还有个方法是replaceState()这个方法接收pushState的前两个参数,重写当前状态,不在历史栈中创建新状态。

history.replaceState({name:"Greg"}, "Greg's page");

这时以后再返回这个页面读到的state.name就是Greg了。
要注意,push进去的每个状态服务器上都要有个真的页面,要不一刷新就404了。
按照逻辑这个pushState应该是在每个web页面刚加载时就push进去,把初始化这个页面的每个数据一起push进去。在当前页面如果有什么即时的修改在下次进入这个页面也该体现的,就使用replaceState。

推荐阅读更多精彩内容