公司需求系列:js封裝一個移動端modal框

安穩.發表於2019-04-12

Created By JishuBao on 2019-04-12 12:38:22
Recently revised in 2019-04-12 17:38:22

 

  歡迎大家來到技術寶的掘金世界,您的star是我寫文章最大的動力!GitHub地址           
感覺已經很久沒寫掘金了,可能是因為閱讀量沒多少把,沒有動力寫,但是我想了想寫部落格主要還是記錄自己在工作中遇到的問題,主要還是以自己學習為主,所以還是寫上一寫吧哈哈哈。

就在今天上午遇到了一個問題,封裝一個很簡單的彈框,需要做到哪個頁面需要這個彈框,直接引入這個Js檔案,執行操作就可以彈出彈框,點選確認彈框消失,因為前面工作中都是以前端框架vue、react為主,已經習慣了那種元件化的開發模式,並且寫元件只需要import一下還是很方便的,但是引入js檔案的這種我說實話還是第一次遇見,畢竟我們公司還沒有做到真正的前後端分離,頁面還是jsp頁面,所以我真的也是很心累的。確實難到了我,所以準備寫下來自己寫好的程式碼~。萬一哪天忘記了還可以再呼叫這樣子,哈哈。大概圖是這樣,真的很簡單~

公司需求系列:js封裝一個移動端modal框

一、理解立即執行函式(function(){})()

之前看了好多程式碼,都有用到這種函式的寫法,但是都沒認真的去想為什麼會這樣寫,今天開始想學習下jquery的原始碼,發現jquery也是使用這種方式,用(function(window, undefined){})(window)包裹內部程式碼,於是進一步的去學習了下。 要理解立即執行函式(function(){})(),先了解些函式的基本概念(函式宣告、函式表示式、匿名函式)。

函式宣告

使用function宣告函式,並指定函式名

function Fn(){
    //coding...
}
複製程式碼

函式表示式

使用function宣告函式,但未指定函式名,將匿名函式賦予一個變數。

var Fn=function(){
    //coding...
}
複製程式碼

匿名函式

使用function關鍵字宣告函式,但未指定函式名。匿名函式屬於函式表示式,匿名函式有很多作用,賦予一個變數則建立函式,賦予一個事件則成為事件處理程式或建立閉包等。

function(){
    //coding
}
複製程式碼

函式宣告與函式表示式的區別

  1. 函式宣告可再當前作用域下提前呼叫執行,函式表示式需等執行到函式後,方可執行,不可提前呼叫。
setFn()
function setFn(){
    //coding
}
//正常,函式宣告可提前呼叫

setFn()
var setFn=function(){
    //coding
}
//報錯 setFn未儲存對函式的引用,函式呼叫需放在函式表示式後面
複製程式碼
  1. 函式表示式可直接在函式後加括號呼叫。
var setFn=function(){
    console.log('setFn') 
}()
//解析至此 可直接執行呼叫,即無需再呼叫,列印出setFn
複製程式碼

立即執行函式

從上面我們可以看到 函式表示式寫法後面加括號可以無需呼叫直接執行

var setFn=function(){
    console.log('setFn') 
}()
//解析至此 可直接執行呼叫,即無需再呼叫,列印出setFn
複製程式碼

簡化就是var setFn=function(){}(),而我們的立即執行函式是(function(){})(),立即執行函式(function(){})()可以看出很像函式表示式的呼叫,但欸什麼要加括號呢?如果不加括號:

function(){
    //coding
}()
//報錯,函式需要函式名
複製程式碼

雖然匿名函式屬於函式表示式,但未進行賦值,所以javascript解析時將開頭的function當作函式宣告,故報錯需要函式名

立即執行函式裡面的函式必須是函式表示式,所以由var setFn = function() {}()可以理解為在匿名函式前加了 = 運算子後,將函式宣告轉化為函式表示式,所以拿!,+,-,()...等運算子來測試下是否如此。

!function(){
    console.log(1)
}()
// 1
    
+function(){
    console.log(2)
}()
// 2
    
-function(){
    console.log(3)
}()
// 3
    
(function(){
    console.log(4)
})()
// 4
複製程式碼

由此可見,加運算子確實可將函式宣告轉化為函式表示式,而之所以使用括號,是因為括號相對其他運算子會更安全,可以減少不必要的麻煩。

立即執行函式與正常函式傳參形式是一致的。

(function(a, b){
    console.log(a + b);
})(1, 2)
// 3
複製程式碼

(function(){}())這樣寫的好處是在內部定義的變數不會跟外部的變數有衝突,達到保護內部變數的作用。

二、編寫彈框

首先新建mymodal.js檔案

(function(window){
	// 使用建構函式方法 宣告一個Modal物件
	var Modal=function(ele){
		this.$el=ele;
        this.init();
	}
	// 在Modal的原型上實現close,open,init方法,實現方法的複用
	Modal.prototype.close=function(){
          this.$el.style.display='none';
    }
	Modal.prototype.open=function(){
          this.$el.style.display='block';
    }
	Modal.prototype.init=function(){
          var self=this;
         // 繫結關閉按鈕點選事件處理函式,檢索所有 帶 .close類名 的按鈕
          if(this.$el.addEventListener){
              this.$el.addEventListener('click',function(e){
                  e.preventDefault();
                  var target=e.target;
                  var classNames=target.className.split(' ');
                  if(classNames.indexOf('close')!==-1){
                      self.close();
                  }
              },false);
          }else if(this.$el.attachEvent){
              this.$el.attachEvent('onclick',function(e){
                      e=e||window.event;
                  if(e.preventDefault){
                      e.preventDefault();
                  }else{
                      e.returnValue=false;
                  }
                  var target=e.target||e.srcElement;
                  var classNames=target.className.split(' ');
                  if(classNames.indexOf('close')!==-1){
                      self.close();
                  }
              });
          }
      }
})(window)
複製程式碼

新建mymodal.html頁面

<!doctype html>
<html>
	<head>
		<meta charset="utf-8"></meta>
		<title>測試彈框demo</title>
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
	</head>
	<body>
		<div class="container">
			<div class="wrapbutton">
				<div class="button" id="button">點選出現彈框按鈕</div>
				<div class="my-modal" id="my-modal" style="display:none;">
					<div class="my-modal-dialog">
						<div class="my-modal-content">今日22:00-15:00都屬於系統維護階段</div>
						<div class="my-modal-controller">
							<div class='my-btn-ok close'>確定</div>
						</div>
                    </div>
				</div>
			</div>
			<script src="mymodal.js"></script>
			<script type="text/javascript">
				document.getElementById('button').onclick=function(){
					var mymodal=new Modal(document.getElementById('my-modal'));
					mymodal.open();
				}
			</script>
			
		</div>
	</body>
	
	<style>
		*{
			margin:0;
			padding:0;
			box-sizing:border-box;
		}
		html,body{
			width:100%;
			height:100%;
		}
		.container{
			width:100%;
			height:100%;
			display:table;
		}
		.container .wrapbutton{
			display:table-cell;
			vertical-align:middle;
			text-align:center;
		}
		.container .button{
			width:250px;
			height:100px;
			background-color:#1890ff;
			font-size:22px;
			border-radius:10px;
			display:inline-block;
			line-height: 95px;
			box-shadow: 4px 3px 5px -1px #1890ff;
			color:white;
			font-weight:500;
			 
		}
		.my-modal{
			position:fixed;
			top:0;
			bottom:0;
			left:0;
			right:0;
			background-color:rgba(0,0,0,.3);
			transform:all .5s
			z-index:10
		}
		.my-modal-dialog{
			width: 60%;
			min-height: 20%;
			border: 1px solid rgb(153,153,153);
			border-radius: 12px;
			position: absolute;
			background-color: #fff;
			top: 50%;
			left: 50%;
			transform: translate(-50%,-50%);
		}
		.my-modal-content{
			padding: 20px 15px;
			line-height: 1.5em;
		}
		.my-modal-controller{
			border-top: 0.5px solid rgba(0,0,0,.3);
			font-size: 20px;
			height: 50px;
			display: table;
			position: absolute;
			width: 100%;
			bottom: 0;
		}
		.my-btn-ok{
			text-align: center;
			display: table-cell;
			vertical-align: middle;
		}
	</style>
	
</html>
複製程式碼

瀏覽器開啟mymodal即可看到如下效果:

點選按鈕出現modal框,點選確定modal消失

公司需求系列:js封裝一個移動端modal框

三、優化結構(寫錯,勿看)

如果這個彈框只有一個頁面用到倒是很好弄,但是公司要求這個彈框需要被很多頁面引用,如果都加一塊div的話難免會出現很多冗雜的程式碼,也不利於後期維護,但是公司用的是傳統的jsp、html不能使用我熟悉的es6語法import 心很累,所以我瞄準了iframe,說實話我面試的時候天天背關於iframe的面試題,即使我根本不知道iframe是什麼東西(笑哭),現在就來使用Iframe優化我們的專案吧!

拆分彈框

  1. 新建modalcomponent.html(名字還是起的比較元件化的),將彈框相關的程式碼拆分到這個頁面
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"></meta>
		<title>彈框元件</title>
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
	</head>
	<body>
				<div class="my-modal" id="my-modal" style="display:none;">
					<div class="my-modal-dialog">
						<div class="my-modal-content">今日22:00-15:00都屬於系統維護階段</div>
						<div class="my-modal-controller">
							<div class='my-btn-ok close'>確定</div>
						</div>
                    </div>
				</div>
	</body>
	
	<style>
		.my-modal{
			position:fixed;
			top:0;
			bottom:0;
			left:0;
			right:0;
			background-color:rgba(0,0,0,.3);
	 
			z-index:10
		}
		.my-modal-dialog{
			width: 60%;
			min-height: 20%;
			border: 1px solid rgb(153,153,153);
			border-radius: 12px;
			position: absolute;
			background-color: #fff;
			top: 50%;
			left: 50%;
			transform: translate(-50%,-50%);
		}
		.my-modal-content{
			padding: 20px 15px;
			line-height: 1.5em;
		}
		.my-modal-controller{
			border-top: 0.5px solid rgba(0,0,0,.3);
			font-size: 20px;
			height: 50px;
			display: table;
			position: absolute;
			width: 100%;
			bottom: 0;
		}
		.my-btn-ok{
			text-align: center;
			display: table-cell;
			vertical-align: middle;
		}
	</style>
	
</html>
複製程式碼
  1. 拆分後的mymodal.html頁面
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"></meta>
		<title>測試彈框demo</title>
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
	</head>
	<body>
		<div class="container">
			<div class="wrapbutton">
				<div class="button" id="button">點選出現彈框按鈕</div>
			</div>
			<script src="mymodal.js"></script>
			<script type="text/javascript">
				document.getElementById('button').onclick=function(){
					var mymodal=new Modal(document.getElementById('my-modal'));
					mymodal.open();
				}
			</script>
			
		</div>
	</body>
	
	<style>
		*{
			margin:0;
			padding:0;
			box-sizing:border-box;
		}
		html,body{
			width:100%;
			height:100%;
		}
		.container{
			width:100%;
			height:100%;
			display:table;
		}
		.container .wrapbutton{
			display:table-cell;
			vertical-align:middle;
			text-align:center;
		}
		.container .button{
			width:250px;
			height:100px;
			background-color:#1890ff;
			font-size:22px;
			border-radius:10px;
			display:inline-block;
			line-height: 95px;
			box-shadow: 4px 3px 5px -1px #1890ff;
			color:white;
			font-weight:500;
			 
		}
	</style>
	
</html>
複製程式碼

通過iframe引入modalcomponent元件

在頁面引入iframe標籤,接下來的要點就是獲取Iframe裡面的元素了,那獲取iframe的元素如何獲取呢,讓我們來看下。

<iframe src="modalComponent" name="modalComponent" id="modalComponent"
				width="0" height="0" frameborder="0" scrolling="no" ></iframe>
複製程式碼

通過百度 我們發現有以下幾種方式

 var iframe = document.getElementById("iframe1");
 var iwindow = iframe.contentWindow;
 var idoc = iwindow.document;
        console.log("window",iwindow);//獲取iframe的window物件
        console.log("document",idoc);  //獲取iframe的document
        console.log("html",idoc.documentElement);//獲取iframe的html
        console.log("head",idoc.head);  //獲取head
        console.log("body",idoc.body);  //獲取body
複製程式碼

另外更簡單的方式是,結合Name屬性,通過window提供的frames獲取.

console.log(window.frames['ifr1'].window);
console.log(document.getElementById("ifr1").contentWindow);
複製程式碼

其實window.frames['ifr1']返回的就是window物件,即

window.frames['ifr1']===window
複製程式碼

當我們準備用console.log(window.frames['modalComponent'].document.getElementById('my-modal'));獲取彈框的Id時,卻出現了這個錯誤!!!

公司需求系列:js封裝一個移動端modal框
百度了下iframe竟然還會跨域 我是對這個Iframe有點心累的!!!

iframe跨域

iframe就是一個隔離沙盒,相當於我們在一個頁面內可以操控很多個標籤頁一樣。如果踩坑的童鞋應該知道,iframe的解決跨域也是很有套套的。

首先我們需要明確什麼是跨域。

瀏覽器判斷你跨沒跨域,主要根據兩個點。 一個是你網頁的協議(protocol),一個就是你的host是否相同,即,就是url的首部:

window.location.protocol +window.location.host
複製程式碼

公司需求系列:js封裝一個移動端modal框
具體的例子就是:

公司需求系列:js封裝一個移動端modal框

需要強調的是,url首部必須一樣,比如:二級域名或者IP地址,都算是跨域.

//域名和域名對應ip, 跨域
http://www.a.com/a.js
http://70.32.92.74/b.js

//統一域名,不同二級域名。 跨域
http://www.a.com/a.js
http://a.com/b.js
複製程式碼

對於第二種情況,iframe可以解決 如:

www.foo.com/a.htmlscript.foo.com/b.html 兩個檔案中分別加上document.domain = ‘foo.com’,指定相同的主域,然後,兩個文件就可以進行互動。

//b.html是以iframe的形式巢狀在a.html中

//www.foo.com上的a.html

document.domain = 'foo.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.foo.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
    var doc = ifr.contentDocument || ifr.contentWindow.document;
    // 在這裡操縱b.html
    alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
};
//script.foo.com上的b.html

document.domain = 'foo.com';
複製程式碼

預設情況下document.domain 是指window.location.hostname. 你可以手動更改,但是最多隻能設定為主域名。 通常,主域名就是指不帶www的hostname, 比如: foo.com , baidu.com 。 如果,帶上www或者其他的字首,就是二級域名或者多級域名。通過上述設定,相同的domain之後,就可以進行同域的相關操作。 另外還可以使用iframe和location.hash,不過由於技術out了,這裡就不做介紹了。

說明:本人興致滿滿的百度寫了好長時間的文章,發現拆分的話使用程式碼實現不了 但是由於百度了一些時間 也就不刪除了 小夥伴只需要看前2章就行了!!

相關文章