最近做項目在前端我使用了很多新技術,這些技術有bootstrap、angularjs,不過最讓我興奮的還是使用了HTML5的技術,今天我想總結一些HTML5的技術,好記性不如爛筆頭,寫寫文章可以很好的整理思路,寫到博客里還能做個備忘。
1) 跨域通訊
現在做企業項目,前端很不自然的會大量使用iframe標簽,我以前在文章里提到iframe是一個效率極其低下的標簽,但是如果項目沒有什么性能的苛求,使用iframe還是非常的方便的。
使用iframe經常碰到父子窗體通訊的問題,我們看看下面的代碼:
父頁面代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>父窗體</title> </head> <body> <span id="superSpan">父窗體</span> <button onclick="pTest()">點擊</button> <iframe src="inner.html" width="300"></iframe> </body> </html> <script type="text/javascript"> var superStr = "父窗體:hello iframe"; function pTest(){ var s = window.frames[0].window.subStr; alert(s); } </script>
子窗體頁面代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>子窗體</title> </head> <body> <span id="subSpan">子窗體</span> <button onclick="sTest()">點擊</button> </body> </html> <script type="text/javascript"> var subStr = "子窗體:OK,Good!"; function sTest(){ var s = parent.superStr; alert(s); } </script>
由上面例子我們可以知道父子窗體可以進行信息的交流,不過這個交流局限性很高,它只能是做到javascript變量和方法的相互交流,如果我們想在父頁面操作子頁面的DOM結構或者子頁面想操作父頁面的DOM結構,這都是不行的(不過這點在我即將要說到的技術也是沒法做到的),另外還有一個非常重要的一點那就是這些操作必須是同域下的。
今天我介紹下HTML5里實現父子窗體通訊的解決方案,它不僅可以在同域下相互發送信息,在不同域名下也是可以相互發送信息的。
同域名下的例子:
父窗體:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>HTML5通訊API</title> </head> <body> <h1>通訊示例,相同域名下</h1> <iframe width="500" src="http://localhost:63342/socketprj/sub.html" onload="test()"></iframe> <div id="showcontent"></div> </body> </html> <script type="text/javascript"> window.addEventListener("message",function(evt){ document.getElementById("showcontent").innerHTML = evt.data; },false); function test(){ var frm = window.frames[0]; frm.postMessage("你好,我是父頁面訪問地址是http://localhost:63342/socketprj/main.html","http://localhost:63342/socketprj/sub.html"); } </script>
子窗體:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>子頁面</title> </head> <body> 子頁面 </body> </html> <script type="text/javascript"> window.addEventListener("message",function(evt){ console.log(evt); document.body.innerHTML = "父頁面過來的數據:" + evt.data; evt.source.postMessage("子頁面回傳過來的信息地址是" + this.location + ",父頁面的地址是" + evt.origin,evt.origin); },false); </script>
這種跨域通訊方式其實就是一個事件,這個事件就是message事件,它是window對象下的一個事件,接收信息就是給window對象綁定message事件,event對象的data屬性可以獲取發送過來的信息,發送信息則是使用postMessage方法了。
如果是跨域,子窗體的代碼不變同上,父窗體代碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>HTML5通訊API</title> </head> <body> <h1>通訊示例,不相同域名下</h1> <iframe width="500" src="http://localhost:8080/sub.html" onload="test()"></iframe> <div id="showcontent"></div> </body> </html> <script type="text/javascript"> window.addEventListener("message",function(evt){ document.getElementById("showcontent").innerHTML = evt.data; },false); function test(){ var frm = window.frames[0]; frm.postMessage("你好,我是父頁面訪問地址是http://localhost:63342/socketprj/main.html","http://localhost:8080/sub.html"); } </script>
2) 瀏覽器的多線程技術:web worker
這里我們先看一個例子吧,這個例子是沒有使用多線程技術做一個超長javascript運算,代碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>對比測試的參照頁面worker</title> </head> <body> <h1>對比測試的參照頁面:javascript執行時間過長會導致瀏覽器終止javascript執行</h1> 請輸入:<input type="text" id="ipt"/> <button onclick="test()">計算</button> </body> </html> <script type="text/javascript"> function test(){ var num = parseInt(document.getElementById("ipt").value,10); var res = 0; for (var i = 0;i < num;i++){ res += 1; } alert(res); } </script>
我們在頁面里輸入999999999999,等一段時間,瀏覽器會彈出一個提示框,如下圖所示:
瀏覽器會提示我們去終止javascript執行,這是為啥了?
瀏覽器內部其實包含兩個執行引擎,一個是渲染引擎,這個引擎負責頁面的展示,一個是javascript引擎,它負責javascript執行,但是瀏覽器在實際執行的方式是以單線程的方式執行渲染操作和javascript操作,也就是說頁面一回只能執行一個操作要么渲染頁面要么就是執行javascript代碼,因此當javascript執行時候就會阻塞渲染的執行,假如javascript執行時間過長頁面加載就會被阻塞,這時候我們就會感覺頁面不可用了,為了避免javascript過長執行導致頁面無法使用,瀏覽器在檢測到javascript執行到一個極限次數時候就會彈出以上提示框。
關於瀏覽器能不能提供多線程的解決方案有人曾經咨詢過javascript之父,這位大師很干脆的說,這是不可能的,他提出的理由是多線程操作過於復雜,那怕是最有經驗的程序員也很難控制好多線程技術,引入它只會增加學習成本和開發風險。
不過話說回來,傳統的瀏覽器單線程改成多線程執行難度還是很大的,因為javascript是有權利修改頁面的展示,如果引入多線程,搞不好程序員就很難正確控制頁面的展示了。
谷歌的工程師為了讓瀏覽器有更好的用戶體驗,在HTML5沒有出現之前做出了一個解決方案,那就是Gear,Gear其實就是web worker的前身,web worker相當於Gear的升級版,下面我們就來看看web worker的使用吧,代碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>worker單線程,無嵌套</title> </head> <body> <h1>worker單線程,無嵌套</h1> 請輸入:<input type="text" id="ipt"/> <button onclick="test()">計算</button> </body> </html> <script type="text/javascript"> var worker = new Worker("js/worker.js"); worker.onmessage = function(evt){ alert(evt.data); } function test(){ var num = parseInt(document.getElementById("ipt").value,10); worker.postMessage(num); } </script>
woker.js的代碼如下:
onmessage = function(evt){ var num = evt.data; var res = 0; for (var i = 0;i < num;i++){ res += 1; // console.log(res); } postMessage(res); }
使用worker技術,我們要把多線程的代碼寫在一個單獨的js文件里面,同時定義一個onmessage方法,這個相當於一個事件的回調函數,回調函數的參數event里的data屬性用來接收數據,postMessage方法傳輸數據,這個做法和前面的message相似。本例子只是演示了一個單線程的例子,我們還可以使用線程嵌套線程,這個我就不細說了,有興趣的朋友可以嘗試一下。
3) 本地存儲
HTML5的web storage技術有兩個一個是sessionstorage和localstorage,sessionstorage顧名思義就是只在會話有效,localstorage則是長時間有效,我在項目里做demo程序時候,就使用localstorage做本地存儲,相當於一個小型數據庫,web storage技術很簡單,我就給一個簡單例子,代碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>本地存儲-存儲數據</title> </head> <body> <h1>本地存儲-存儲數據</h1> </body> </html> <script type="text/javascript"> localStorage.setItem("mess","Hello Message!"); localStorage.setItem("obj",{id:"111",name:"xtq"}); </script>
在chrome瀏覽器里我們可以在控制台里Resource的local Storage找到存儲的數據,我們可以發現obj最終存儲的是[object,object],這就說明web storage只能存儲字符串,如果我們想存儲javascript對象就的將其序列化變成字符串進行存儲。
4) 多文件上傳
文件上傳使用的標簽就是:
<input type="file" multiple id="fileIpt" size="120"/>
在html4里,這個標簽只能上傳一個文件,現在我們只要在標簽里加入multiple屬性就可以實現多文件上傳。在html4里ajax是沒有辦法做上傳文件操作,因此我們如果想實現ajax文件上傳就不得不使用hack技術,模擬文件上傳,這個我曾在博客里分享過我寫的模擬多文件上傳的demo,當時也是沒法子要保證瀏覽器兼容性,不得不hack一把了。
在html5里我們對文件上傳的控制力變得更強了,今天只展示一個簡單文件上傳操作的API,今后我會寫一個復雜的多文件上傳的demo和大家一起分享下,我們先看下面這個例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>多文件上傳</title> </head> <body> <input type="file" multiple id="fileIpt" size="120"/><br/> <button onclick="test()">點擊測試</button> </body> </html> <script type="text/javascript"> function test(){ var f = document.getElementById("fileIpt"); var sFileList = ""; for (var i = 0;i < f.files.length;i++){ var item = f.files[i]; sFileList += "文件名稱:" + item.name + "\n修改時間:" + item.lastModifiedDate + "\n文件大小:" + item.size + "\n文件類型:" + item.type + "\n=================\n"; } alert(sFileList); } </script>
在我做一個圖片上傳項目的時候,曾經有個想法就是想在圖片客戶端這里,也就是在文件傳輸到服務器之前給文件一個預覽功能,當時我沒時間仔細查閱資料,因此自己還是按照HTML4里掌握的知識認為瀏覽器在客戶端是無法操作文件數據的,因此很難實現這個想法。最近看書發現原來HTML5已經可以做到這一點了,我寫了一個例子和大家分享下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>圖片本地顯示</title> </head> <body> <input type="file" multiple id="fileIpt" size="120"/><br/> <button onclick="test()">顯示圖片</button> <div id="result"></div> </body> </html> <script type="text/javascript"> function test(){ var files = document.getElementById("fileIpt").files; for (var i = 0;i < files.length;i++){ var f = files[i]; if (f.type.indexOf("image") != -1){ var reader = new FileReader(); reader.readAsDataURL(f); reader.onload = function(evt){ var resDiv = document.getElementById("result"); var oImg = document.createElement("img"); oImg.setAttribute("src",this.result); oImg.setAttribute("width",300); oImg.setAttribute("height",300); resDiv.appendChild(oImg); } } } } </script>
好了,今天文章就寫到這里,對於多文件上傳我之后一定抽時間寫個完整的例子出來,自己上次做項目對這塊做的很是過癮,而今天還發現了可以在客戶端預覽圖片,因此這塊很值得寫個好demo了。