mui初級入門教程(四)— 再談webview,從小白變「大神」!

文章來源:小青年原創
發佈時間:2016-06-05
關鍵詞:mui,html5+,webview
轉載需標註本文原始地址: http://zhaomenghuan.github.io...javascript

寫在前面

前段時間羣裏某網友的問題,彈出菜單被子頁面擋住了這個老生常談的問題,其實只要明白webview常見的層級問題,這個問題很容易解釋,那麼解決方案天然很容易想到,若是沒有理解錯,html5+裏面webview的建立規則是後來居上原則,因此若是想解決那個問題,有兩種解決辦法:css

  • 將彈出菜單放在子頁面裏面,而後父子頁面之間傳值,這種方法實用於單個子頁面的狀況,對於多頁面可能並不方便。html

  • 第二個方法是將彈出菜單放在一個webview裏面,設置爲透明背景,這樣就能夠在保證在最上面同時能夠蓋住底部的內容,在android上建立菜單webview的時候設置background"transparent"能夠實現,可是html5+ webview .WebviewStyles中說iOS平臺不支持「transparent」背景透明樣式,默認背景使用白色背景。因爲沒有用蘋果測試過,我真的信了,昨天在羣裏有人再次問這個問題時,我覺得蘋果不支持因此說這種方法存在兼容性,然而有人說能夠,囧。。。被人呵呵了,實話說有點小受傷,不過也是由於本身沒有測就下告終論,這樣確實也很差。前端

但是這個問題仍是會有人去問,因此想一想也沒什麼,就把webview的其餘內容再補充一下,這篇文章不會再貼文檔,純粹作實驗,咱們從新認識一下5+中的webview,若是對於文章中提到的一些方法不熟悉的能夠看看html5+ webview 文檔html5

WebviewObject 對象詳解

今天咱們先來從新認識一下webview,實踐是檢驗真理的惟一標準,咱們經過作實驗來試試,其中WebviewObject對象是很特別的一個對象,我相信對於這個對象的理解,能夠幫助咱們理解webview的一些細節,咱們就詳細看看這個對象。java

id屬性

首先談談WebviewObject對象的id屬性,相信你們必定熟悉id選擇器,id選擇器是最經常使用的選擇器之一,咱們經過document.getElementById(id)就能夠可返回對擁有指定 ID 的第一個對象的引用,作過android開發的必定知道findViewById經過這個方法能夠獲得控件對象的引用,相信5+中的plus.webview.getWebviewById(id)應該是將原生中的方法進行了封裝以便於使用JavaScript調用。在打開或建立Webview窗口時設置,若是沒有設置窗口標識,此屬性值爲當前應用的APPID,字符串類型。注意,若是是在HBuilder真機運行獲取的是固定值「HBuilder」,須要提交App雲端打包後運行才能獲取真實的APPID值。node

獲取當前窗口id:android

var ws=plus.webview.currentWebview();
console.log( "窗口標識: "+ws.id );

咱們首先由id這個概念才能更加靈活管理webview,好比經過id獲取對象關閉窗口:ios

var ws = plus.webview.getWebviewById(id);
plus.webview.close(ws);

等效於:git

plus.webview.getWebviewById(id).close();

其餘的方法相似,具體的能夠參考文檔 → 5+ webview

窗口層疊關係

咱們應該注意到每建立一個webview至關於在當前屏幕建立多個重疊的頁面層,因此這裏的層疊關係是怎麼樣的呢?咱們不妨作一個實驗:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <title></title>
</head>
<body>
    <script type="text/javascript" charset="utf-8">
          // H5 plus事件處理
        function plusReady(){
            var ws1=null,ws2=null,ws3=null;
            
            // 獲取當前全部Webview窗口
            var ws1=plus.webview.currentWebview();
            console.log("webview"+plus.webview.all().length+":"+ws1.id);
    
            setTimeout(function(){
                ws2=plus.webview.create("http://weibo.com/dhnetwork");
                ws2.show();
                console.log("webview"+plus.webview.all().length+":"+ws2.id);
            },100);
            
            setTimeout(function(){
                ws3=plus.webview.create("http://zhaomenghuan.github.io/");
                ws3.show();
                console.log("webview"+plus.webview.all().length+":"+ws3.id);
            },3000);
            
            setTimeout(function(){
                ws3.close();
                console.log("剩餘窗口數量:"+plus.webview.all().length)
            },6000);
            
            setTimeout(function(){
                ws2.close();
                console.log("剩餘窗口數量:"+plus.webview.all().length)
            },9000);
        }
        
        if(window.plus){
            plusReady();
        }else{
            document.addEventListener("plusready",plusReady,false);
        }
    </script>
</body>
</html>

在控制檯會打印這個:

webview1:HBuilder at index.html:16
 webview2:http://weibo.com/dhnetwork at index.html:21
 webview3:http://zhaomenghuan.github.io/ at index.html:27
 剩餘窗口數量:2 at index.html:32
 剩餘窗口數量:1 at index.html:37

其實執行完這個你們就明顯能夠看出點結論,在層級關係上你能夠認爲webview後來居上原則,咱們能夠經過控制webview建立銷燬、顯示隱藏實現頁面切換。咱們建立的多個webview實際上是在原生android中的一個activity上,webview之間的頁面切換有別於原生android中的activity間的跳轉。不少人沒有搞清楚webview這個後來居上原則就會亂用一些方法也會有一些搞不清楚的問題:好比:

  • 若是父webview上的彈出菜單被子webview擋住了怎麼解決?

  • mui中的openWindow()當成href`跳轉使用,形成屢次重複建立頁面出現閃屏。

  • 如何實現按下返回鍵不會退到上一個頁面(經常使用於註冊登陸這一個場景的)

。。。

這裏我沒法一一列舉,可是能夠說說通用的一些東西,當咱們對於html5+ webview的基本方法很熟悉了,咱們能夠再看看mui中的back()openWindow()方法的實現思路,天然對於這些問題就會理解了。

那咱們如今再作一個系列實驗,剛剛咱們是經過一層層的註銷對象,並且是用的網絡地址,如今咱們在本地建立兩個頁面。

咱們新建一個項目,建立一個index.html文件:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <title></title>
</head>
<body>
    <script type="text/javascript" charset="utf-8">
          // H5 plus事件處理
        function plusReady(){
            var ws = plus.webview.create("ws1.html");
            ws.show();
        }
        if(window.plus){
            plusReady();
        }else{
            document.addEventListener("plusready",plusReady,false);
        }
    </script>
</body>
</html>

再新建ws1.html文件:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    這是第二個webview
    <script type="text/javascript">
        // H5 plus事件處理
        function plusReady(){
            setTimeout(function(){
                var ws = plus.webview.create("ws2.html");
                ws.show();
            },3000)
        }
        if(window.plus){
            plusReady();
        }else{
            document.addEventListener("plusready",plusReady,false);
        }
    </script>
</body>
</html>

再新建ws2.html文件:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    這是第三個webview
    <script type="text/javascript">
        function plusReady(){
            plus.webview.getWebviewById("ws1.html").close();
            
            // 獲取全部Webview窗口
            var wvs=plus.webview.all();
            for(var i=0;i<wvs.length;i++){
                console.log("webview"+i+": "+wvs[i].id);
            }
            for(var i=0;i<wvs.length;i++){
                console.log("webview"+i+": "+wvs[i].opener());
            }
        }
        if(window.plus){
            plusReady();
        }else{
            document.addEventListener("plusready",plusReady,false);
        }
    </script>
</body>
</html>

結果:

webview0: HBuilder at ws2.html:16
webview1: ws2.html at ws2.html:16
webview0: undefined at ws2.html:19
webview1: null at ws2.html:19

咱們在第三個webview裏面經過id把第二個webview正常關閉了,這說明咱們的webview是能夠控制的,經過plus.webview.all()方法能夠獲取當前全部的webview對象,發現被關閉的webview被銷燬了。

這裏咱們須要說明的是被咱們關閉的webview,咱們沒法經過opener()獲取當前Webview窗口的建立者,返回值爲null,這有別於undefined

有個問題咱們一直都沒有去探索,就是這些webview對象之間有沒有什麼關係呢?咱們在第二篇說到了父子webview,那麼這裏的幾個webview之間是否是父子結構呢,咱們不妨試試看,咱們經過WebviewObjectparent()獲取父窗口,這裏咱們把ws2.html中修改以下:

for(var i=0;i<wvs.length;i++){
    console.log("webview"+i+": "+wvs[i].parent());
}

結果以下:

webview0: undefined at ws2.html:16
webview1: undefined at ws2.html:16

這說明咱們建立的這幾個對象是平行關係,不存在父子關係,文檔中提到:Webview窗口做爲子窗口添加(Webview.append)到其它Webview窗口中時有效,這時其它Webview窗口爲父窗口。

那咱們將第三個webview填充到第二個webview中試試,咱們將ws1.html修改以下:

function plusReady(){
    var ws = plus.webview.create("ws2.html","",{top:"46px",bottom:"0px"});
    plus.webview.currentWebview().append(ws);
}

ws2.html修改以下:

function plusReady(){
    // 獲取全部Webview窗口
    var wvs=plus.webview.all();
    for(var i=0;i<wvs.length;i++){
        console.log("webview"+i+": "+wvs[i].parent());
    }
}

輸入以下:

webview0: undefined at ws2.html:16
webview1: undefined at ws2.html:16
webview2: [object Object] at ws2.html:16

很明顯咱們發現第三個webview具備父對象,這裏也說明了父子對象的應用場景,將另外一個Webview窗口做爲子窗口添加到當前Webview窗口中,添加後其全部權歸父Webview窗口,父窗口顯示時子窗口會自動顯示,父窗口隱藏時子窗口自動隱藏,當父窗口關閉時子窗口也自動關閉。

咱們不妨在子webview關閉父webview試試,結果發現子webview也被關閉了,若是不對子webview進行close()方法操做,可知子webview的生命週期是由父webview決定的。咱們能夠經過對子webview進行show()hide()操做,甚至可使用remove移除子Webview窗口,從而實現動態子webview。這種場景最經常使用的是webview選項卡。

頁面歷史記錄操做

不少人有將wap站點打包成APP需求,官方也提供了一些教程,只是不少人沒有搞清楚思路,沒有明白改造的基本方法,其中有個最多見的問題是如何經過返回鍵控制網頁內容的回退,論壇上有些回答說經過setJsFile引入mui.js,而後重寫mui.back(),其實這個回答原本沒有錯,可是這個答案依然會有不少細節問題,說直接就是mui.back()怎麼去重寫的問題,在搞懂這些問題以前咱們不妨看看咱們html5+有什麼解決辦法,因爲mui並無給出前端路由的相關解決方法,使用前端技術寫app,總以爲在頁面管理上會有點混亂,前幾天一直在構思基於html5+mui的前端路由解決方案。感受這個問題若是深刻探究須要另開篇再談,這裏先給出一個最簡單的將一個網址打包成app的方案,這個在應用商店估計是通不過的,只是一個基本思路,你們須要的能夠拿去看看。

能夠經過plus.key.addEventListener來註冊監聽返回按鍵backbutton事件:

plus.key.addEventListener("backbutton",function(){
    alert( "BackButton Key pressed!" );
});

經過WebviewObject對象的canBackcanForward方法能夠查詢Webview窗口的狀態,經過backforward控制頁面加載。

  • canBack: 查詢Webview窗口是否可後退

  • canForward: 查詢Webview窗口是否可前進

  • back: 後退到上次加載的頁面

  • forward: 前進到上次加載的頁面

  • clear清除原生窗口的內容,用於重置原生窗口加載的內容,清除其加載的歷史記錄等內容

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <title></title>
</head>
<body>
    <script type="text/javascript" charset="utf-8">
        var ws=null,nw=null;
        function plusReady(){
            ws=plus.webview.currentWebview();
            nw=plus.webview.create("http://weibo.com/dhnetwork");
            ws.append(nw);
            plus.key.addEventListener("backbutton",function(){
                //查詢Webview窗口是否可後退
                nw.canBack( function(e){
                    var canback=e.canBack;
                    if(canback){
                        nw.back();
                    }else{
                        back();
                    }
                });
            });
        }
        
        var first = null;
        var back = function() {
            if (!first) {
                first = (new Date()).getTime();
                plus.nativeUI.toast('再按一次退出應用');
                setTimeout(function() {
                    first = null;
                }, 1000);
            } else {
                if ((new Date()).getTime() - first < 1000) {
                    plus.runtime.quit();
                }
            }
        }
        
        if(window.plus){
            plusReady();
        }else{
            document.addEventListener("plusready",plusReady,false);
        }
    </script>
</body>
</html>

若是是使用mui,咱們能夠這樣寫:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <title></title>
</head>
<body>
    <script src="js/mui.min.js"></script>
    <script type="text/javascript" charset="utf-8">
          mui.init();
          
          var ws=null,nw=null;
          mui.plusReady(function () {
              ws=plus.webview.currentWebview();
            nw=plus.webview.create("https://www.baidu.com/");
            ws.append(nw);
          });
          
          var _back = mui.back;
          mui.back = function(){
              //查詢Webview窗口是否可後退
            nw.canBack( function(e){
                var canback=e.canBack;
                if(canback){
                    nw.back();
                }else{
                    _back();
                }
            });
          }
    </script>
</body>
</html>

固然WebviewObject對象的內容不止這些,這裏我只列舉了其中你們容易出問題的一些內容,詳細的講解再後面的其餘模塊的講解中會再作補充,你們也能夠本身學習。

WebviewStyles對象舉例

WebviewStylesJSON對象,原生窗口設置參數的對象,設置方法有兩種:

  • plus.webview.create()或者plus.webview.open()中做爲參數設置,如:

plus.webview.create(url,id,{
    top: '0px',
    bottom: '0px',
    mask: 'rgba(0,0,0,0.5)'
})
  • 使用setStyle()動態設置,如:

var ws=plus.webview.currentWebview();
// 顯示遮罩層
ws.setStyle({mask:"rgba(0,0,0,0.5)"});

WebviewStyles經常使用屬性

zindex: (Number 類型 )窗口的堆疊順序值
擁有更高堆疊順序的窗口老是會處於堆疊順序較低的窗口的上面,擁有相同堆疊順序的窗口後調用show方法則在前面。

設置方法如:

{
    zindex: 999
}

前面咱們講到了webview的層疊關係是後來居上原則,有一個前提是默認層級,沒有使用zindex改變默認的層級關係值,你們知道在web中咱們使用z-index能夠改變控件的層疊關係,說直觀點就好是能夠定義有重疊的兩個部分,誰在上面誰在下面的問題。

那麼在必要的時候咱們可使用這個WebviewStyleszindex屬性去改變層疊關係。

clipboard.png

如上圖這種狀況,咱們的底部菜單中間有個突出的部分,若是直接用咱們以前將到的那個方法,咱們知道要麼是那個突起的一小塊被擋住了,要麼是中間的內容與底部非得保持必定的距離,這就尷尬了,並且目前官方的demo沒有給這種特殊的需求,那麼是否是不能本身作呢?

確定不是的,否則html5plus也不至於要作爲一個標準去提,確定是有通用方法解決不一樣的個性化需求的。

咱們很好想到的就是在上次的基礎上進行改進就能夠知足這個需求:咱們首先思考一個問題,咱們是將父webview的堆疊順序改爲比子頁高仍是說將tabbar做爲一個層獨立出來呢?

首先咱們思考一下簡單粗暴的提升父webview會怎麼樣?首先咱們會發現子webview會被父webview蓋住,有人可能會說把父webview搞成透明的,OK,那麼問題來了怎麼設置透明呢?

background: (String 類型 )窗口的背景顏色
窗口空白區域的背景模式,設置background爲顏色值(參考CSS Color Names,可取值/十六進制值/rgb值/rgba值),窗口爲獨佔模式顯示(佔整個屏幕區域); 設置background爲「transparent」,則表示窗口背景透明,爲非獨佔模式。

設置方法如:

{
    background: 'transparent'
}

這個就是我開篇說到的那個問題,當時犯了錯誤,誤覺得ios不支持,後來獲得果汁的確認後來支持了,文檔沒有更新而已。

雖然咱們能夠經過設置父webview透明不至於看不到子頁面,可是有個更嚴重的問題就是子webview被蓋住了咱們不能操做,這是多麼的坑,因此這個方法走不通。咱們想一下把tabbar抽離出來做爲一個子webview,只要設置tabbar的範圍,也就是說不全屏幕鋪開,設置高度屬性限制高度,設置bottomtabbar固定在底部,而後設置tabbarzindex屬性比其餘子webview高就ok了。

// 子頁參數
var Index = 0;
var subpages = ['html/home.html','html/message.html','html/find.html','html/setting.html'];
var subpage_style = {
    top: '45px',
    bottom: '50px',
    zindex:99
};

// 底部導航欄
var tabbar = "html/tabbar.html";
var tabbar_style = {
    height:"60px",
    bottom:"0px",
    background: "transparent",
    zindex:999
}

咱們只須要給tabbar設置backgroundzindex就能夠實現上面那個圖那種效果,可是也所以帶了一個問題就是,咱們以前點擊底部欄直接就能夠獲取相關的參數進行切換,可是咱們如今把tabbar單獨拿出來了,那麼久涉及一個父子webview通訊的問題,咱們前面一篇文章講到頁面初始化時候經過擴展參數extras傳值,這裏咱們須要用到自定義事件,經過自定義事件,用戶能夠輕鬆實現多webview間數據傳遞。

咱們這裏先貼出tabbar的局部代碼便於講解,你們能夠在【mui demo】倉庫下載完整代碼,或者在這裏c查看【預覽效果】

在此感謝羣友們提出demo中的bug,就是在蘋果手機中切換tabbar,tabbar依然會被擋住的問題,開篇講到因爲本人沒有蘋果手機,因此demo不免在蘋果手機上有一些小問題,可是問題仍是能夠解決的,其實看了文章多思考一下本身沒啥問題的,不建議新手直接一上來來demo,否則出現小問題本身都不知道什麼緣由,那麼這篇文章也沒啥意義。再來講說羣友提的問題,我以爲有兩種可能性,沒有用蘋果作實驗測試,只是猜測:1.zindex在ios無效;2.優先級的問題,多是選項卡切換過程執行show方法,咱們前面說到後來居上原則,這個時候zindx的層級關係優先級低於後來居上原則。你們能夠去實驗驗證,這裏沒法給出確定答案。不過解決思路,我以爲能夠試試兩種:1.在子頁面show後立刻用setStyle設置zindex;2.直接在子頁面show後從新執行tabbar show方法。

tabbar.html
html部分:

<nav class="mui-bar mui-bar-tab mui-botton-bar">
    <a class="mui-tab-item mui-active" href="html/home.html">
        <img class="mui-icon" src="../img/i-home-active.png"/>
        <span class="mui-tab-label">首頁</span>
    </a>
    <a class="mui-tab-item" href="html/message.html">
        <img class="mui-icon" src="../img/i-star.png"/>
        <span class="mui-tab-label">消息</span>
    </a>
    <a class="mui-tabbar-center" href="popover.html">
        <img src="../img/i-pop-active.png"/>
    </a>
    <a class="mui-tab-item" href="html/find.html">
        <img  class="mui-icon" src="../img/i-find.png"/>
        <span class="mui-tab-label">發現</span>
    </a>
    <a class="mui-tab-item" href="html/setting.html">
        <img class="mui-icon" src="../img/i-person.png"/>
        <span class="mui-tab-label">我的</span>
    </a>
</nav>

js部分:

//選項卡點擊事件
mui('.mui-bar-tab').on('tap', 'a', function(e) {
    // 獲取當前點擊的選項
    var targetTab = this.getAttribute('href');    
    // 若是點擊中間的菜單欄彈出菜單
    if(targetTab == popTab){
        // 建立mask遮罩
        plus.webview.create("","mask",{
            mask:"rgba(0,0,0,0.4)",
            background: "transparent"
        }).show();
        // 打開彈出層
        plus.webview.show(popWebview,"slide-in-bottom",300);
        return;
    }
    
    //當前選項值傳到父webview
    var currWs = plus.webview.currentWebview();    
    var targetTitle = this.querySelector('.mui-tab-label').innerHTML;
    //觸發詳情頁面的newsId事件
    mui.fire(currWs.parent(),'targetTab',{
        targetTitle:targetTitle,
        targetTab:targetTab
    });
    
    /**
     * 下面這部分非每一個項目必須的,由於這裏爲了給
     * 你們演示怎麼用圖片做爲圖標而不用字體圖標。
     */    
    // 獲取圖標對象
    var targetIcon=mui(this.children[0])[0];
    //初始化
    mui('.mui-bar-tab .mui-tab-item img').each(function (index,item) {
        var itemSrc = item.getAttribute('src');
        if(itemSrc.indexOf('active')){
            item.src = itemSrc.replace('-active.png','.png');
        }
    });
    //設置當前的圖標
    targetIcon.src = targetIcon.getAttribute('src').replace('.png','-active.png');    
});

這裏咱們使用了圖片而不是圖標,由於考慮有些項目可能設計比較個性化,咱們不必定能夠在Iconfont-阿里巴巴矢量圖標庫上找到合適的圖,有時候用字體文件有侷限性,因此這個例子裏面咱們使用了圖片演示。

這個地方須要對幾個細節特別說說:

mui.fire()觸發自定義事件

按照文檔的說明咱們知道有三個參數:(不知道文檔在哪裏的請戳這裏【文檔】

mui.fire( target , event , data )

target爲你要傳入數據的那個webview,咱們這裏是要出入到父webview,因爲咱們沒有給父webview指定id,咱們前面知道這樣就不方便拿到父webview對象,這裏就使用當前webviewparent()間接獲取。
event是你能夠指定的自定義事件名稱。
data是你要傳入的數據,爲json格式 (不知道json爲什麼物的同窗請戳我上一篇文章mui初級入門教程(三)— html5+ XMLHttpRequest 與mui ajax用法詳解) 。

咱們獲取數據也很簡單:

// 添加targetTab自定義事件監聽
window.addEventListener('targetTab',function(event){
      // 得到選項卡點擊事件參數
      var targetTitle = event.detail.targetTitle;
      var targetTab = event.detail.targetTab;
      //接下來這裏拿到數據後寫邏輯代碼了...
});

不過這裏有一個特別須要注意的問題,因爲我尚未遇到,可是看官網文檔有說明,貼出來方便後來遇到這個問題的同窗:

目標webview必須觸發loaded事件後才能使用自定義事件

若新建立一個webview,不等該webview的loaded事件發生,就當即使用webview.evalJS()或mui.fire(webview,'eventName',{}),則可能無效;案例參考:這裏

mui對象和DOM對象的區別

之因此說說這個是由於在寫那個用圖片代碼字體圖標的時候出現一個問題就是選中當前選項,選項卡圖片要換成對應激活狀態的圖片。那麼問題來了,怎麼拿到a標籤下的img標籤對象,若是用過jQuery,咱們知道直接用下面的代碼就能夠實現:

$('.mui-bar-tab a').children("img").css("src","xxx-active.png");

然而mui本着極簡的原則沒有childrencss方法,那麼咱們只考慮用原生js操做DOM。這裏推薦你們用mui和原生js實現,畢竟就那麼一點代碼引入一個庫不值得,也不利於提升自身的水平。那麼咱們這裏就補一下基礎知識,考慮到每一個人基礎不一樣,這裏儘量精簡的說一下。

HTML DOM 基礎

什麼是 DOM?
DOM ,全稱Document Object Model(文檔對象模型),是 W3C(萬維網聯盟)的標準。DOM 定義了訪問 HTML 和 XML 文檔的標準:

「W3C 文檔對象模型 (DOM) 是中立於平臺和語言的接口,它容許程序和腳本動態地訪問和更新文檔的內容、結構和樣式。」

W3C DOM 標準被分爲 3 個不一樣的部分:

  • 核心 DOM - 針對任何結構化文檔的標準模型

  • XML DOM - 針對 XML 文檔的標準模型

  • HTML DOM - 針對 HTML 文檔的標準模型

什麼是 HTML DOM?
HTML DOM 是HTML 的標準對象模型和標準編程接口,W3C 標準。HTML DOM 定義了全部 HTML 元素的對象和屬性,以及訪問它們的方法。換言之,HTML DOM 是關於如何獲取、修改、添加或刪除 HTML 元素的標準。

HTML DOM 節點樹
HTML DOM 將 HTML 文檔視做樹結構。這種結構被稱爲節點樹:

節點父、子和同胞:
節點樹中的節點彼此擁有層級關係。
父(parent)、子(child)和同胞(sibling)等術語用於描述這些關係。父節點擁有子節點。同級的子節點被稱爲同胞(兄弟或姐妹)。

HTML DOM 屬性
屬性是節點(HTML 元素)的值,您可以獲取或設置。可經過 JavaScript (以及其餘編程語言)對 HTML DOM 進行訪問。

1.innerHTML 屬性:獲取元素內容的最簡單方法是使用 innerHTML 屬性。
innerHTML 屬性對於獲取或替換 HTML 元素的內容頗有用。

2.nodeName 屬性:nodeName 屬性規定節點的名稱,是隻讀的,nodeName 始終包含 HTML 元素的大寫字母標籤名。

  • 元素節點的 nodeName 與標籤名相同

  • 屬性節點的 nodeName 與屬性名相同

  • 文本節點的 nodeName 始終是 #text

  • 文檔節點的 nodeName 始終是 #document

3.nodeValue 屬性:nodeValue 屬性規定節點的值。

  • 元素節點的 nodeValue 是 undefined 或 null

  • 文本節點的 nodeValue 是文本自己

  • 屬性節點的 nodeValue 是屬性值

4.nodeType 屬性:nodeType 屬性返回節點的類型, 是隻讀的。
比較重要的節點類型有:

元素類型 NodeType
元素 1
屬性 2
文本 3
註釋 8
文檔 9

5.childNodes屬性與children屬性
childNodes 屬性返回包含被選節點的子節點的 NodeList。若是選定的節點沒有子節點,則該屬性返回不包含節點的 NodeList。childNodes包含的不只僅只有html節點,全部屬性,文本、註釋等節點都包含在childNodes裏面。children只返回元素如input, span, script, div等,不會返回TextNode,註釋。

HTML DOM方法
一般使用的最多的就是 Document和 window 對象。簡單的說, window 對象表示瀏覽器中的內容,而 document 對象是文檔自己的根節點。Element 繼承了通用的 Node 接口, 將這兩個接口結合後就提供了許多方法和屬性能夠供單個元素使用。在處理這些元素所對應的不一樣類型的數據時,這些元素可能會有專用的接口。下面是在web和XML頁面腳本中使用DOM時,一些經常使用的方法:

方法 描述
getElementById() 返回帶有指定 ID 的元素。
getElementsByTagName() 返回包含帶有指定標籤名稱的全部元素的節點列表(集合/節點數組)。
getElementsByClassName() 返回包含帶有指定類名的全部元素的節點列表。
appendChild() 把新的子節點添加到指定節點。
removeChild() 刪除子節點。
replaceChild() 替換子節點。
insertBefore() 在指定的子節點前面插入新的子節點。
createAttribute() 建立屬性節點。
createElement() 建立元素節點。
createTextNode() 建立文本節點。
getAttribute() 返回指定的屬性值。
setAttribute() 把指定屬性設置或修改成指定的值。

具體更詳細的你們能夠參考這篇文章JavaScript DOM——「節點層次」的注意要點

mui對象和dom對象的具體區別

咱們上面講了一下DOM對象的基本屬性和方法,限於篇幅,只是簡單說了說,若是說連上面的都不知道的就須要查一下咯,固然DOM歷史悠久,確定不止這麼多內容,對於新手來講熟悉經常使用的DOM是頗有必要的,我本身在這方面目前就作得不夠好,後期還會繼續深刻學習。

首先咱們得說mui對象和dom對象都咱們是兩個對象,都有本身的獨有的屬性和方法,若是一個對象調用了一個本身沒有另一個對象有的屬性和方法,確定會報錯的。這裏咱們先舉個小例子:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script src="../js/mui.js" type="text/javascript" charset="utf-8"></script>
        <button type="button" id="btn">按鈕</button>
        <script type="text/javascript">
            // dom對象實現
            var btn = document.getElementById("btn");
            btn.addEventListener('click',function(){
                console.log("用dom對象獲取button元素點擊了按鈕")
            });
            // mui對象實現
            mui("#btn")[0].addEventListener('click',function(){
                console.log("用mui對象獲取button元素點擊了按鈕")
            });
            //判斷這兩個方法是否等同
            console.log(document.getElementById("btn")===mui("#btn")[0])
        </script>
    </body>
</html>

常常容易犯錯的一個就是下面的使用方法:

mui("#btn").addEventListener('click',function(){
    console.log("用mui對象獲取button元素點擊了按鈕")
});

咱們經過mui("#btn")獲取的是一個mui對象實例,然而mui對象沒有addEventListener,就會報錯,咱們用jQuery也會出現這種問題,然而jQueryon方法很經常使用,不會想那麼多,可是mui中的on方法實現批量元素的事件綁定,非得傳入第二個參數,和咱們的需求有時候不相符合,mui推薦咱們直接使用addEventListener方法,然而這個是dom對象的方法,咱們又不想用document.getElementById("btn")這種一長串的方法怎麼辦呢?咱們能夠考慮將mui對象轉成dom對象,方法是:mui對象mui("#btn")轉成dom對象爲mui("#btn")[0],咱們後面也會陸續講到怎麼本身封裝一些經常使用的dom對象操做方法。

咱們接下來講說mui對象中的on()中的this指向和父子節點問題:

mui('.mui-bar-tab').on('tap', 'a', function(e) {
    console.log(this.innerHTML)
})

咱們這裏執行這個會發現這裏的this指向的是當前點擊的a標籤。

咱們若是想獲取子節點,咱們前面提到了有兩個方法:childNodeschildren,咱們能夠用下面的方面遍歷:

mui('.mui-bar-tab').on('tap', 'a', function(e) {
    for(var i=0;i<this.childNodes.length;i++){
        console.log("childNodes:"+this.childNodes[i]);
    }
    
    for(var i=0;i<this.children.length;i++){
        console.log("children:"+this.children[i])
    }
})

結果:

childNodes:[object Text] at html/tabbar.html:85
childNodes:[object HTMLImageElement] at html/tabbar.html:85
childNodes:[object Text] at html/tabbar.html:85
childNodes:[object HTMLSpanElement] at html/tabbar.html:85
childNodes:[object Text] at html/tabbar.html:85
children:[object HTMLImageElement] at html/tabbar.html:89
children:[object HTMLSpanElement] at html/tabbar.html:89

咱們會發現使用childNodes會有[object Text],咱們這裏的實際上是由於咱們上面的HTML結構中有回車和空格的緣由,去掉後會發現和children結果一致。

注:Internet Explorer 下使用childNodes會忽略節點之間生成的空白文本節點(好比換行字符)。

爲了簡單咱們一般使用children,好比上面的例子咱們用到了

var targetIcon=mui(this.children[0])[0];

這樣咱們就拿到了HTMLImageElement對象,咱們經過getAttribute('src')方法就能夠拿到src屬性。

其實寫到這裏咱們發現只要掌握了原生js操做DOM的方法,咱們其實能夠不過分依賴jQuery這種庫,固然jQuery也不只僅只是這麼多內容,不少封裝的思路值得咱們去學習,小白學習前端的路很長,可是必定要腳踏實地去落實,不要急於求成。

項目實戰之父子頁面彈出層

這一篇文章原本上次就應該完成的,可是一直拖了好久,一來是由於時間久了,這麼問題感受也不想繼續寫,畢竟寫文章要花時間,可是又有強迫症,想一想仍是完善一下,詳不詳細都沒關係,
可是要把主要的內容寫出來就能夠。

先看效果圖:

clipboard.png

就是前面咱們點擊中間那個選項彈出的一個菜單,很顯然這個問題具備必定的表明性。作過相似需求的朋友確定知道問題所在,咱們把彈出菜單若是放在父webview,那麼在這種狀況下會被子webview蓋住,固然咱們能夠考慮在點擊彈出層時候動態設置父頁面的層級比子頁面高,而後關閉再設置恢復,可是這個過程很麻煩,不是最佳實戰方法,在子webview的話,那麼設計父子webview通訊的問題,對於這種多子webview頁面的狀況是否是過於麻煩呢,這種時候咱們用新建一個webview裝彈出層我以爲是一種最合適的方案。

知道思路了,方案實施很簡單的,其實就是當咱們點擊那個彈出層的時候,而後顯示webview,當關閉的時候隱藏或者關閉webview。打開時候的關鍵代碼以下:

//彈出菜單
var menuWebview;
var menuTab = 'menu.html';
mui.plusReady(function(){    
    //預加載彈出菜單子頁面
    menuWebview = mui.preload({
        url:menuTab,
        id:menuTab,
        styles:{
            top: '0px',
            bottom: '0px',
            background: 'transparent'
        }
    });
})

//...此處略去若干代碼

// 若是點擊中間的菜單欄彈出菜單
if(targetTab == menuTab){
    if(window.plus){
        // 建立mask遮罩
        plus.webview.create("","mask",{
            mask:"rgba(0,0,0,0.4)",
            background: "transparent"
        }).show();
        // 打開彈出層
        plus.webview.show(menuWebview,"slide-in-bottom",300);    
    }else{
        mui.alert("請在html5+引擎環境使用");
    }
    return;
}

咱們這裏作了一些特別的處理,咱們設置彈出層webview中的background: 'transparent',以及彈出層頁面的body{background: transparent;}是爲了獲得一個透明的彈出層,若是不須要能夠忽略,同時能夠能夠經過設置topbottom設置彈出層的範圍,這些具體配置參數在上面的內容中都有講解,具體的你們能夠詳細看看。另外考慮到有人須要遮罩這種佈局,咱們專門新建了一個webview建立mask遮罩,具體的參數相似。

至於關閉彈出層也很簡單,咱們在彈出層的頁面重寫mui.back()方法。

/*
 * 這裏重寫mui.back()方法,在須要執行關閉命令的地方
 * 加上 mui-action-back 類,能夠綁定back()方法。
*/
mui.back = function(){
    // 隱藏彈出層
    plus.webview.currentWebview().hide();
    // 關閉遮罩
    plus.webview.getWebviewById('mask').close();
}

至此咱們這個彈出層是算完美解決了。

另外不少人老是嘗試去關閉webview,其實webview開着的時候真正的佔多少內存呢,你打開瀏覽器就知道,不會說你開了幾頁面就被卡死了,固然暴力操做和頁面內面阻塞錯誤除外,不過通常瀏覽器也好像限制了頁面打開的數量,我手機自帶的瀏覽器是最多能夠打開15個窗口。因此咱們儘可能不要開啓過多的webview,可以使用單頁去代替的能夠考慮單頁。這裏有個div模式的tabbar切換動畫:【demo傳送門】。另外webview不建議都關閉,若是後面會用到的webview能夠用hide()代替,同時即便要關閉,也不適宜一次性關閉,常常看到有人用all查找當前的webview,用循環一次性關閉,形成內存溢出。我首先不想說底層的原理實現,就想用常識想一想,你打開webview的時候須要執行操做,那麼關閉的時候就不執行操做嗎?你同時一會兒作那麼多事,手機瀏覽器是否是都被你佔用了執行關閉操做,那麼這個過程難道不須要內存消耗嗎?你根據id分時去close天然會好得多,有時候咱們出現問題先考慮一下是否是本身的方式不對。

寫在後面

其實回過頭來再看看其實內容並不算多,也不是很複雜,爲啥依然有那麼多抱怨呢,說來講去不按套路出牌,不少人用mui徹底可是不按mui的思路,想固然的去作,開發前文檔都不看,出問題了也不懂緣由,其實有時候再噴的時候能不能把那個時間拿來看看文檔再說。我一貫主張是作事前先花時間去搞清楚一些基本規則,花時間去學習,而後再去作事就不會花不少冤枉時間;可是我經常看到的是不少人花不多的時間學習,而後花不少時間去填坑。這種狀況一般時間也花了很多,可是沒有什麼長進,更談不上深入理解。(徹底我的意見,不滿能夠忽略)

文章原始地址是我博客地址:

http://zhaomenghuan.github.io

【MUI從入門到精通】專欄地址:

https://segmentfault.com/blog...

mui demo地址:

https://github.com/zhaomenghu...


寫文章不容易,也許寫這些代碼就幾分鐘的事,寫一篇你們好接受的文章或許須要幾天的醞釀,而後加上幾天的碼字,累並快樂着。若是文章對您有幫助請我喝杯咖啡吧!

clipboard.png


近期在segmentfault講堂開設了一場關於html5+ App開發工程化實踐之路的講座,會講到5+ 開發中高性能的優化方案以及使用如何結合Vue.js進行開發,歡迎前來圍觀:https://segmentfault.com/l/15...

相關文章
相關標籤/搜索