跨文档消息传递:(cross-document message)
有时候简称XDM,指的是来自不同域的页面间传递消息;
XDM的核心是postMessage()方法,目的是向另一个地方传递数据,对于XDM而言,另一个地方指的是包含值本页面的iframe元素,或则由当前页面弹出的窗口;
postMessage()接收两个参数,一条消息和一个表示消息接收方来自哪个域的字符串,第二个参数对保障安全通讯非常重要,可以防止浏览器把消息发送到不安全的地方。
<body> <iframe id="iframe" src="http://www.tzding.com" height="300px" width="300px" scrolling="yes"></iframe> <script> var iframe=document.getElementById("iframe"); var iframeWindow=iframe.contentWindow; iframeWindow.postMessage("a secret","*");//来自任何域,不推荐 iframeWindow.postMessage("a secret","http://www.baidu.com");//来自百度 </script> </body>
接收XDM消息时,会触发window对象的message事件,是异步触发的,会有延迟,传递给message事件的事件对象包含三个重要信息:
data:作为postMessage()第一个参数传入的字符串数据;
origin:发送消息的文档所在的域,例如:"http://www.baidu.com"
source:发送消息的文档的window对象的代理。这个代理对象主要用于在发送上一条消息的窗口调用postMessage()方法。如果发送消息的窗口来自同一个域,那这个对象就是window。
收到消息后验证发送窗口的来源是至关重要的,就像给postMessage()方法指定第二个参数,以确保浏览器不会把消息发送给未知页面一样,在onmessage处理程序中检测消息来源可以确保传入的消息来自已知页面。基本验证模式如下:
window.addEventListener("window","message",function(event){
//确保发送消息的域是已知的域
if(event.origin=="http://www.baidu.com"){
//处理接收到的数据
console.log(event.data);
//可选:向来源窗口发送回执
event.source.postMessage("Received!","http://www.baidu.com")
}
})
postMessage()方法的第一个参数必须是字符串,也可以传其它结构数据,调用JSON.stringify(),在onmessage事件处理程序中,用JSON.parse()
将json字符串转回成json格式;
不过iframe正在淡出前端开发,因为它有很多的缺点,不如ajax,了解一下;
原生拖放:
被拖动元素:
dragstart:按下鼠标键并开始移动瞬间,触发。
drag:dragstart触发后紧接着触发drag事件,拖动过程中持续触发。
dragend:拖动停止时,无论放置在了有效放置目标还是无效放置目标,都会触发dragend事件。
当把某个元素放置在有效放置目标上时触发的事件:
dragenter:只要有元素被拖到放置目标上就会触发dragenter事件。
dragover:dragenter事件紧接着就会触发dragover事件,被拖动元素在放置目标上移动时,持续触发该事件。
dragleave:当拖动元素被拖出放置目标时,触发dragleave事件。
drop:当拖动元素放置在了放置元素内时,触发drop事件。
自定义放置目标:
自定义放置目标,只需要将该元素的dragenter和dragover事件的默认行为阻止就行;(当拖动元素移动到该目标时,光标会显示可被放置的样式)
自定义拖放元素:
只需要将该元素的draggable属性改成true就行;而文本(必须选择)和图片默认可以拖动;
例子:
<body> <div class="box"></div> <div draggable="true" class="imgBox"></div> <script> var img=document.getElementsByClassName("imgBox")[0]; var box=document.getElementsByClassName("box")[0]; box.addEventListener("dragover",function(event){ event.preventDefault(); console.log("dragover事件"); },false) box.addEventListener("dragenter",function(event){ event.preventDefault(); console.log("dragenter事件"); },false) box.addEventListener("dragleave",function(event){ console.log("dragleave事件"); },false) box.addEventListener("drop",function(event){ console.log("drop事件"); },false); img.addEventListener("dragstart",function(event){ console.log("dragstart事件"); },false); img.addEventListener("drag",function(event){ console.log("drag事件"); },false); img.addEventListener("dragend",function(event){ console.log("dragend事件"); },false);
dataTransfer对象:
只有在拖动事件处理程序中,事件对象含有dataTransfer对象,这个对象包括两个方法:setData(),getData()方法;
setData():接收两个参数,第一个参数可以问“text”和“URL”,“text”:表示设置一个文本类型的值,“URL”表示设置一个url类型的值;第二个参数就是要设置的对应的文本或url,都为字符串形式;
getData():保存在dataTransfer对象中的值,只有在drop事件处理程序中读取,这个方法的唯一一个参数就是setData()方法的第一个参数,“text”或“URL”;
如果在firfox浏览器下,最好将getData()方法的参数写成“url”||“text/uri-list”或者"Text";这是因为火狐的解析问题;
下面实现一个,拖动一个元素到另一个盒子内,放置时,往放置目标内加入文本或图片:
<div class="box"></div> <div draggable="true" class="imgBox"></div> <script> var img=document.getElementsByClassName("imgBox")[0]; var box=document.getElementsByClassName("box")[0]; box.addEventListener("dragover",function(event){ event.preventDefault(); console.log("dragover事件"); },false) box.addEventListener("dragenter",function(event){ event.preventDefault(); console.log("dragenter事件"); },false) box.addEventListener("dragleave",function(event){ console.log("dragleave事件"); },false) box.addEventListener("drop",function(event){ console.log("drop事件"); //放置文本进去 // var text=event.dataTransfer.getData("text"); // var textNode=document.createTextNode(text); // box.appendChild(textNode); // 放置一个图片进去 var img=document.createElement("img"); var url=event.dataTransfer.getData("text"); img.src=url; box.appendChild(img); },false); img.addEventListener("dragstart",function(event){ console.log("dragstart事件"); // 设置一个文本 // event.dataTransfer.setData("text","哈哈哈"); // 设置一个图片 event.dataTransfer.setData("text","./images/home.png"); },false); img.addEventListener("drag",function(event){ console.log("drag事件"); },false); img.addEventListener("dragend",function(event){ console.log("dragend事件"); },false);
dropEffect与effectAllowed:
利用dataTransfer对象,不光能够传输数据,还能通过它来确定被拖动元素以及作为放置目标的元素能够接收什么操作。为此,需要访问dataTransfer对象的两个属性:dropEffect和effectAllowed。
其中,通过dropEffect属性可以知道被拖动元素能够执行哪种拖动行为,这个属性有下列4个可能值:
“none”:不能把拖动元素放在这里。这是除文本框之外的所有元素的默认值;
“move”:应该把拖动的元素移动到放置目标;
“copy”:应该把拖动的元素复制到放置的目标;
“link”:表示放置目标会打开拖动的元素(但拖动的元素必须是一个连接,有URL);
在把元素拖动到放置目标上时,以上每一个值都会光标显示不同的符号,如果自己不介入,没什么会自动的移动、复制、也不会打开连接;总之,浏览器只能帮您改变鼠标的样式,而具体产生什么效果,要考自己实现;
另外dropEffect属性必须在ondragenter事件处理程序中针对放置目标来设置;
dropEffect属性只能搭配effectAllowed属性才有用。effectAllowed属性表示允许拖动元素的哪种dropEffect,effectAllowed属性可能值如下:
“uninitialiaed”:没有给被拖动元素设置任何放置行为;
“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元素的默认放置事件(drop),什么都不做,所以需要自己编写代码,将文本插入到div元素中。如果将dropEffect和effectAllowed设置为“copy”,那么不会自动移走文本框中的文本;
dataTransfer对象的其它属性:
addElement(element):为拖动操作添加一个元素。只有firefox3.5+实现了这个方法;
clearData(format):清除以特定格式保存的数据,IE、firefox3.5+、chrome、safari4+都实现了这个方法;
setDragImage(element,x,y):指定一副图像,当拖动发生时,显示在光标下方。三个参数分别表示,要显示的HTML元素、和光标在图像中的x,y坐标。是图像则显示图像,是元素则显示渲染后的元素;firefox3.5+、safari4+、chrome都实现的这个方法;
types:当前保存的数据类型;这是一个类似数组的集合,以“text”这样的字符串形式保存着数据类型。实现这个属性的浏览器有IE10+、firefox3.5+和chrome。
媒体元素:
大多数提供富媒体内容的站点为了保证跨浏览器兼容性,不得不使用Flash,HTML5新增了两个与媒体相关的标签,让开发人员,不必依赖任何插件就能在网页中嵌入跨浏览器的音频和视频内容。这两个标签就是<audio>和<video>。
<!-- 嵌入视频 --> <video class="video" src="./Wildlife.wmv" poster="./images/home.png" controls width="400" height="200">video player not available.</video> <!-- 嵌入音频 --> <audio class="audio" src="./Kalimba.mp3" controls>Audio player not available</audio>
使用这两个元素,至少要包含src属性,指定要加载的媒体文件。还可以给视频播放器加width和height属性。而poster属性指定图像的URI可以在加载视频期间显示一副图像。另外,标签中如果有controls属性,意味着浏览器应该显示UI控件。以便用户直接操作媒体;位于开始和结束标签之间的内容是在浏览器不支持这个元素时显示。
因为并非所有浏览器都支持所有媒体格式,所以可以指定多个不同的媒体来源。为此,不用再标签中指定src属性,而是像下面这样使用一个或多个<source>元素:
一个可以播放的视频连接:http://www.welcometofillory.com/videos/landing-video.mp4
<!-- 嵌入视频 --> <video id="myVideo" poster="./images/home.png" controls width="400" height="200"> <source src="conference.webm" type="video/webm;codes='vp8,vorbis'" > <source src="conference.ogv" type="video/ogg;codes='theora,vorbis'"> <source src="conference.mpg"> video player not available. </video> <!-- 嵌入音频 --> <audio id="myAudio" controls> <source src="./Kalimba.mp3" type="audio/ogg"> <source src="./Kalimba.mp3" type="audio/mpeg"> Audio player not available </audio>
一般来说指定多种格式的媒体来源是必须的,支持者两个媒体元素的浏览器有:IE9+、firefox4+、opera10.5+、chrome、ios版safari和android版webkit。
属性:
<video>和<audio>元素都提供了完善的javascript接口。下表列出了这两个元素共有的属性,通过这些属性可以知道媒体的当前状态。
<audio> 标签属性:
- src:音乐的URL
- preload:预加载
- autoplay:自动播放
- loop:循环播放
- controls:浏览器自带的控制
<video> 标签属性:
- src:视频的URL
- poster:视频封面,没有播放时显示的图片
- preload:预加载
- autoplay:自动播放
- loop:循环播放
- controls:浏览器自带的控制条
- width:视频宽度
- height:视频高度
获取HTMLVideoElement和HTMLAudioElement对象
1 //audio可以直接通过new创建对象 2 Media = new Audio("http://www.abc.com/test.mp3"); 3 //audio和video都可以通过标签获取对象 4 Media = document.getElementById("media");
Media方法和属性:HTMLVideoElement 和 HTMLAudioElement 均继承自 HTMLMediaElement
1 //错误状态 2 Media.error; //null:正常 3 Media.error.code; //1.用户终止 2.网络错误 3.解码错误 4.URL无效 4 5 //网络状态 6 Media.currentSrc; //返回当前资源的URL 7 Media.src = value; //返回或设置当前资源的URL 8 Media.canPlayType(type); //是否能播放某种格式的资源 9 Media.networkState; //0.此元素未初始化 1.正常但没有使用网络 2.正在下载数据 3.没有找到资源 10 Media.load(); //重新加载src指定的资源 11 Media.buffered; //返回已缓冲区域,TimeRanges 12 Media.preload; //none:不预载 metadata:预载资源信息 auto: 13 14 //准备状态 15 Media.readyState; //1:HAVE_NOTHING 2:HAVE_METADATA 3.HAVE_CURRENT_DATA 4.HAVE_FUTURE_DATA 5.HAVE_ENOUGH_DATA 16 Media.seeking; //是否正在seeking 17 18 //回放状态 19 Media.currentTime = value; //当前播放的位置,赋值可改变位置 20 Media.startTime; //一般为0,如果为流媒体或者不从0开始的资源,则不为0 21 Media.duration; //当前资源长度 流返回无限 22 Media.paused; //是否暂停 23 Media.defaultPlaybackRate = value;//默认的回放速度,可以设置 24 Media.playbackRate = value;//当前播放速度,设置后马上改变 25 Media.played; //返回已经播放的区域,TimeRanges,关于此对象见下文 26 Media.seekable; //返回可以seek的区域 TimeRanges 27 Media.ended; //是否结束 28 Media.autoPlay; //是否自动播放 29 Media.loop; //是否循环播放 30 Media.play(); //播放 31 Media.pause(); //暂停 32 33 //控制 34 Media.controls;//是否有默认控制条 35 Media.volume = value; //音量 36 Media.muted = value; //静音 37 38 //TimeRanges(区域)对象 39 TimeRanges.length; //区域段数 40 TimeRanges.start(index) //第index段区域的开始位置 41 TimeRanges.end(index) //第index段区域的结束位置
事件:
1 eventTester = function(e){ 2 Media.addEventListener(e,function(){ 3 console.log((new Date()).getTime(),e); 4 }); 5 } 6 7 eventTester("loadstart"); //客户端开始请求数据 8 eventTester("progress"); //客户端正在请求数据 9 eventTester("suspend"); //延迟下载 10 eventTester("abort"); //客户端主动终止下载(不是因为错误引起), 11 eventTester("error"); //请求数据时遇到错误 12 eventTester("stalled"); //网速失速 13 eventTester("play"); //play()和autoplay开始播放时触发 14 eventTester("pause"); //pause()触发 15 eventTester("loadedmetadata"); //成功获取资源长度 16 eventTester("loadeddata"); // 17 eventTester("waiting"); //等待数据,并非错误 18 eventTester("playing"); //开始回放 19 eventTester("canplay"); //可以播放,但中途可能因为加载而暂停 20 eventTester("canplaythrough"); //可以播放,歌曲全部加载完毕 21 eventTester("seeking"); //寻找中 22 eventTester("seeked"); //寻找完毕 23 eventTester("timeupdate"); //播放时间改变 24 eventTester("ended"); //播放结束 25 eventTester("ratechange"); //播放速率改变 26 eventTester("durationchange"); //资源长度改变 27 eventTester("volumechange"
可以使用这旷工媒体元素的play()和pause()方法制作一个简单的媒体播放器;
历史状态管理:
历史状态管理是现代web应用开发中的一个难点。在现代web应用中,用户的每一次操作不一定会打开一个全新的页面,因此后退和前进按钮就失去了作用,导致用户很难在不同状态间切换。要解决这个问题,首选使用hashChange事件(当url的hash值改变时触发)。HTML5通过更新history对象为管理历史状态提供了方便。
通过hashChange事件,可以知道URL的参数什么时候发生了变化,即什么时候有所反应。而通过状态管理API,能够在不加载新页面的情况下改变浏览器的URL。为此,需要使用history.pushState()方法,该方法可以接受三个参数:状态对象、新状态的标题、可选的相对URL。
注意:第三个参数URL一定要和原来的url同源!!!!
例如:
window.addEventListener("hashchange",function(event){//url的hash值改变时触发
console.log(window.location.hash);
},false);
history.pushState({name:"firstPage"},'','file:///C:/Users/%E5%86%AF%E5%BA%86%E6%B5%B7/Desktop/vue-cli3/home/test.html#3');
执行pushState()方法后,新的历史状态信息就会被加入历史状态栈,而浏览器地址也会变成新的相对URL。但是,浏览器并不会真的向服务器发送请求,即使状态改变之后查询location.href也会返回与地址栏中相同的地址。另外,第二个参数目前还没有浏览器实现,因此完全可以直传一个空字符串,或者一个短标题亦可以。而第一个参数则尽可能提供初始化页面状态所需的各种信息。
因此pushState()会创建新的历史状态,所以你会发现后退按钮也可以使用了。按下后退按钮,会触发window对象的popstate事件。popstate事件的事件对象有一个state属性,这个属性就包含着当初以第一个参数传递给pushState()的状态对象。
window.addEventListener("hashchange",function(event){//url的hash值改变时触发
console.log(window.location.hash);
},false);
history.pushState({name:"firstPage"},'','file:///C:/Users/%E5%86%AF%E5%BA%86%E6%B5%B7/Desktop/vue-cli3/home/test.html#3');
console.log(history);
//点击后退按钮后会触发popstate事件,此事件对象中的state属性包含着pushState传过来的状态对象
window.addEventListener("popstate",function(event){
console.log(event.state);
},false);
得到这个状态对象后,必须报页面重置为状态对象中的数据表示状态(因为浏览器不会自动为你做这些)。记住,浏览器加载的第一个页面没有状态,因此单击后退按钮返回浏览器加载的第一个页面时,event.state值为null。
要更新当前状态,可以调用replaceState()。传入的参数与pushState()的前两个参数相同。调用这个方法不会再历史状态栈中创建新状态,只会重写当前状态。
window.addEventListener("hashchange",function(event){//url的hash值改变时触发
console.log(window.location.hash);
},false);
history.pushState({name:"firstPage"},'','file:///C:/Users/%E5%86%AF%E5%BA%86%E6%B5%B7/Desktop/vue-cli3/home/test.html#3');
console.log(history);
//重写当前状态
history.replaceState({name:"new state"},'');
//点击后退按钮后会触发popstate事件,此事件对象中的state属性包含着pushState传过来的状态对象
window.addEventListener("popstate",function(event){
console.log(event.state);
},false);
此时,第一次pushState()传入的状态就会被重写;但不会创建新的历史记录;
支持HTML5历史状态管理的浏览器有Firefox4+、safari5+、opera11.5+、chrome。
在safari和chrome中,传递给pushState()或replaceState()的状态对象中不能包含dom元素。而firefox支持在状态函数中包含DOM元素。opera还支持history.state属性,它返回当前状态的状态对象。