一步步教你實現彈出窗口(第3部分)

作者: 司徒正美  來源: 博客園  發布時間: 2009-11-08 15:53  閱讀: 2223 次  推薦: 0   原文鏈接   [收藏]  

  這部分的內容是綁定事件,模擬模態窗口與拖動。先從最簡單的說起,彈出窗口現在有三個按鈕,反正都是點擊事件,我們可以利用事件代理以節省偵聽器。偵聽器放在頂層容器中就是,然后我們再判定冒泡上來的事件的源對象的標簽是否為a元素,再根據其類名添加相應的事件。

      container.onclick = function(){
        var ee = me.getEvent(), node = ee[1],tag = ee[2];
        if(tag == "a" ){
          switch(node.className){
            case "closebtn" :            
              me.hide();
              break;
            case "positive" :
              me.hide();
              //form.submit();
              break;
            case "negative" :
              alert("你點擊了取消按鈕!");
              break;
          }
        }
      }

  其中me即為this,Dialog類的實例,因為就這樣簡單把this暴露在綁定函數中,它對應的是container這個頂層容器,而不是實例。hide方法的功能很簡單,隱藏頂層容器。getEvent方法的具體代碼可參見我的另一篇博文,用來快捷取得事件,事件源對象以及源對象的標簽名。到此,綁定事件部分講完了。

  再看拖放,我以前有兩篇博文專門說這問題,可參見這里這里。現在扼要演實一下如何運用。首先在init()主方法中添加如下代碼:

      container.onmousedown = function(e){
        e = e || event;
        container.offset_x = e.clientX - container.offsetLeft;
        container.offset_y = e.clientY - container.offsetTop;
        document.onmousemove = function(e){
          me.drag(e,container)
        }
        document.onmouseup = function(){
          me.dragend(container)
        }
      }

  然后再添加兩個原型方法,分別為drag與dragend。

drag:function(e,el){
      e = e || event;//獲得事件對象
      var l = e.clientX - el.offset_x  + "px",
      t = e.clientY - el.offset_y  + "px";
      this.incss(el, {cursor:"move",left:l,top:t})
        !+"\v1"? document.selection.empty() : window.getSelection().removeAllRanges();
    },
    dragend:function(el){
      el.style.cursor = "";
      document.onmouseup = document.onmousemove = null;
    }

  因為我原來的動態添加樣式規則方法css名副其實,就是動態添加,不具備修改能力,因此對于在拖動中頻繁修改的CSS屬性就無能為力了,于是我又用回內聯樣式。它的代碼很簡單,以前我也多次演示過這東西。

  incss:function(node,bag){
      var str = ";"
      for(var i in bag){
        if(bag.hasOwnProperty(i))
          str += i+":"+bag[i]+";"
      }
      node.style.cssText = str;
    }

  如果IE6死掉是多么美好了,基本上就是這樣簡單。我們在IE6中拖啊拖,遇到select標簽了!為了掩住了它,我們需要一個iframe標簽,并把它放到頂層容器之前(它與頂層容器是兄弟節點)。由于我們的彈出窗口是有陰影的,因此iframe的大小應該為彈出窗口加陰影的大小,并且其z-index比它頂層容器一點。

      if(me.ie6){
        me.iframe = document.createElement("");
        container.insertAdjacentElement('beforeBegin',me.iframe);
      }

  在拖動窗口時也拖動iframe。

 drag:function(e,me){
      e = e || event;//獲得事件對象
      var el = me.container,
      l = e.clientX - el.offset_x  + "px",
      t = e.clientY - el.offset_y  + "px";
      this.incss(el, {cursor:"move",left:l,top:t})
      if(this.ie6){
        with(me.iframe.style){//當用cssText設置iframe的樣式時,其display變為inline,就無法掩住select
          left=l;                   //另,當設置iframe的allowTransparency="true" ,也不能掩住select
          top=t;
        }
      }
        !+"\v1"? document.selection.empty() : window.getSelection().removeAllRanges();
    },

  最看模態窗口的實現。現在所有瀏覽器都支持showModeDialog方法了,即使是火狐。但在首次使用此方法,IE會有提示是否允許執行。若用戶以前做夠了彈出廣告的騷擾,我們再用這方法來實現,恐怕就行不通了。因此我是使用純DIV來模擬。首先是凍結窗口,把可視區外部分隱藏掉,阻止滾動條出現。其次阻止彈出窗口外的其他部分的右鍵事件與選擇事件。

mode:function(w,h){
      var mask = Dialog.mask,me = this;
      //IE6的addRule方法存在bug,不支持聯合選擇器,即html,body{*********}這種形式,只好分開寫!
      this.css("html","margin:0;border:0;width:100%;height:100%;overflow:hidden;");
      this.css("body","margin:0;border:0;width:100%;height:100%;overflow:hidden;");
      this.incss(mask,{position:"absolute",background:"#fff",top:0,left:0,
        width:w +"px",height:h +"px","-moz-user-select":"none"});
        !+"\v1"? (mask.style.filter = "alpha(opacity=0)") : (mask.style.opacity = "0");
      mask.onselectstart = function(e){//不允許選擇彈出窗口以外的內容
        me.stopEvent(e);
      }
      mask.oncontextmenu = function(e){//不允許在彈出窗口外彈出右鍵窗口
        me.stopEvent(e);
      }
    },

  mode方法我們在show與hide中調用。

    hide : function(){
      this.container.style.display = "none" ;
      if(this.ie6){
        this.iframe.style.display = "none";
      }
      this.mode(0,0);
      //下面兩行目的是生成 html,body{width:auto;height:auto;overflow:auto;}
      this.incss(document.body, {width:"auto",height:"auto",overflow:"auto"});
      this.incss(document.documentElement, {width:"auto",height:"auto",overflow:"auto"});
    },
    show : function(){
      this.container.style.display = "block" ;
      if(this.ie6){
        this.iframe.style.display = "block";
      }
      var size = this.getBrowserWindowSize();
      this.mode(size.width, size.height);
    },

  stopEvent方法是用來阻止事件冒泡與瀏覽器的默認事件,一個非常常用的方法。

 

 stopEvent:function(e){
      e = e || window.event;
      if(e.preventDefault) {
        e.preventDefault();
        e.stopPropagation();
      }else{
        e.returnValue = false;
        e.cancelBubble = true;
      }
    },

 

  基本上是這樣,300多行,為了兼容Opera的圓角與陰影,有30多行分配給它,為了修正IE6的select bug,有10行分配給它,為了讓標準瀏覽器擁有IE風格的設置樣式規則的方法,又要20行。其他的兼容代碼估計也有二十行左右。因此瀏覽器這東西還是一家獨大好了。我這個彈出窗口其實還有許多改進之處,如把圓角半透明邊框獨立出來,作為一個接口,那個動態添加樣式的也可以再擴展一下,獨立出來,做成另一個接口,再把模態化與拖動獨立出來,然后像mootools那樣實現類好了,超優雅。

0
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()