🔥手撸JS系列:移动端弹框
Created By JishuBao on 2019-04-12 12:38:22 Recently revised in 2019-04-12 17:38:22
就在今天上午遇到了一个问题,封装一个很简单的弹框,需要做到哪个页面需要这个弹框,直接引入这个Js文件,执行操作就可以弹出弹框,点击确认弹框消失,因为前面工作中都是以前端框架vue、react为主,已经习惯了那种组件化的开发模式,并且写组件只需要import一下还是很方便的,但是引入js文件的这种我说实话还是第一次遇见,毕竟我们公司还没有做到真正的前后端分离,页面还是jsp页面,所以我真的也是很心累的。确实难到了我,所以准备写下来自己写好的代码~。万一哪天忘记了还可以再调用这样子,哈哈。大概图是这样,真的很简单~

一、理解立即执行函数(function(){})()
之前看了好多代码,都有用到这种函数的写法,但是都没认真的去想为什么会这样写,今天开始想学习下jquery的源码,发现jquery也是使用这种方式,用(function(window, undefined){})(window)包裹内部代码,于是进一步的去学习了下。 要理解立即执行函数(function(){})(),先了解些函数的基本概念(函数声明、函数表达式、匿名函数)。
函数声明
使用function声明函数,并指定函数名
function Fn(){
//coding...
}
函数表达式
使用function声明函数,但未指定函数名,将匿名函数赋予一个变量。
var Fn=function(){
//coding...
}
匿名函数
使用function关键字声明函数,但未指定函数名。匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等。
function(){
//coding
}
函数声明与函数表达式的区别
- 函数声明可再当前作用域下提前调用执行,函数表达式需等执行到函数后,方可执行,不可提前调用。
setFn()
function setFn(){
//coding
}
//正常,函数声明可提前调用
setFn()
var setFn=function(){
//coding
}
//报错 setFn未保存对函数的引用,函数调用需放在函数表达式后面
- 函数表达式可直接在函数后加括号调用。
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.Modal=Modal;
})(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消失

三、优化结构(写错,勿看)
如果这个弹框只有一个页面用到倒是很好弄,但是公司要求这个弹框需要被很多页面引用,如果都加一块div的话难免会出现很多冗杂的代码,也不利于后期维护,但是公司用的是传统的jsp、html不能使用我熟悉的es6语法import 心很累,所以我瞄准了iframe,说实话我面试的时候天天背关于iframe的面试题,即使我根本不知道iframe是什么东西(笑哭),现在就来使用Iframe优化我们的项目吧!
拆分弹框
- 新建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>
- 拆分后的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时,却出现了这个错误!!!

iframe跨域
iframe就是一个隔离沙盒,相当于我们在一个页面内可以操控很多个标签页一样。如果踩坑的童鞋应该知道,iframe的解决跨域也是很有套套的。
首先我们需要明确什么是跨域。
浏览器判断你跨没跨域,主要根据两个点。 一个是你网页的协议(protocol),一个就是你的host是否相同,即,就是url的首部:
window.location.protocol +window.location.host


需要强调的是,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.html 和 script.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章就行了!!
转载自:https://juejin.cn/post/6844903817507504141