HTML5脚本编程

JavaScript 2020-07-22 824

1.跨文档消息传递

简称XDM,指的是在不同域的页面间传递消息。如在www.wrox.com 域中的页面与位于一个内嵌框架的p2p.wrox.com进行通信。XDM的核心是postMessage()方法。在H5规范中,除了 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.com"。
 source:发送消息的文档的 window 对象的代理。这个代理对象主要用于在发送上一条消息的窗口中调用 postMessage()方法。如果发送消息的窗口来自同一个域,那这个对象就是window。
接收到消息后验证发送窗口的来源是至关重要的。就像给 postMessage()方法指定第二个参数,以确保浏览器不会把消息发送给未知页面一样,在 onmessage 处理程序中检测消息来源可以确保传入的消息来自已知的页面。基本的检测模式如下。

window.addEventListener('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 也可以在来自相同域的页面间使用。

2.原生拖放

2.1拖放事件

拖动某元素时,将依次触发下列事件:
1.dragstart
2.drag
3.dragend
拖动开始时,可以通过ondragstart事件处理程序来运行JS代码。
触发dragstart事件后,随即触发drag事件,且在元素被拖放期间会持续触发该事件。在鼠标移动过程中,mousemove 事件也会持续发生。当拖动停止时(无论是把元素放到了有效的放置目标,还是放到了无效的放置目标上),会触发 dragend 事件。
当某个元素被拖放到一个有效的放置目标上时,下列事件会依次发生:
(1)dragenter
(2)dragover
(3)dragleave或drop
只要有元素被拖动到放置目标上,就会触发 dragenter 事件(类似于 mouseover 事件)。紧随其后的是 dragover 事件,而且在被拖动的元素还在放置目标的范围内移动时,就会持续触发该事件。如果元素被拖出了放置目标,dragover 事件不再发生,但会触发 dragleave 事件(类似于 mouseout事件)。如果元素被放到了放置目标中,则会触发 drop 事件而不是 dragleave 事件。♥上述三个事件的目标都是作为放置目标的元素。

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事件了。

2.3 dataTransfer事件

在拖放操作时实现数据交换,可以使用dataTransfer对象,它只能由事件对象访问。
该对象有两个方法,setData()和getData(),这两个方法都只接受1个参数,是一个字符串,表示保存的数据类型,取值为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"。
在拖动文本框中的文本时,浏览器会调用 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");  

注意,一定要把短数据类型放在前面,因为 IE 10 及之前的版本仍然不支持扩展的 MIME 类型名,而它们在遇到无法识别的数据类型时,会抛出错误。

2.4 dataEffect与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",那就不会自动移走文本框中的文本。

2.5可拖动

为元素添加draggable=“true”属性,表示元素可以拖动。

<div id="one" draggable="true" class="item">项目1</div>  
2.6 dataTransfer其他属性和方法

 addElement(element):为拖动操作添加一个元素。添加这个元素只影响数据(即增加作为拖动源而响应回调的对象),不会影响拖动操作时页面元素的外观。
 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。

3.媒体元素

音频标签<audio>,视频标签<video>

<!-- 嵌入视频 -->  
<video src="conference.mpg" id="myVideo">Video player not available.</video>  
<!-- 嵌入音频 -->  
<audio src="song.mp3" id="myAudio">Audio player not available.</audio>  

更多信息,点击链接
4.历史状态管理
历史状态管理是现代 Web 应用开发中的一个难点。在现代 Web 应用中,用户的每次操作不一定会打开一个全新的页面,因此“后退”和“前进”按钮也就失去了作用,导致用户很难在不同状态间切换。

要解决这个问题,首选使用 hashchange 事件(第 13 章曾讨论过)。HTML5 通过更新 history 对象为管理历史状态提供了方便。

通过 hashchange 事件,可以知道 URL 的参数什么时候发生了变化,即什么时候该有所反应。而通过状态管理 API ,能够在不加载新页面的情况下改变浏览器的 URL 。为此,需要使用history.pushState()方法,该方法可以接收三个参数:状态对象、新状态的标题和可选的相对 URL。例如:

history.pushState(null, null, '/blog/blog_list/')  

执行 pushState()方法后,新的状态信息就会被加入历史状态栈,而浏览器地址栏也会变成新的相对 URL。但是,浏览器并不会真的向服务器发送请求(仅仅只是修改了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 属性,它返回当前状态的状态对象。
这个方法可以实现那些,滚动到锚点位置时,改变浏览器的url。

标签:JavaScript

文章评论

评论列表

已有0条评论