手把手教你用原生Javascript封装一个Dialog组件

下来玩玩

Dialog component by native javascript

分析

对外暴露的方法

  • show(options) 显示dialog
    • options 参数
      • title 标题,默认为“”,不显示标题
      • content 主内容,默认为"兄弟,你好像忘记传content的值了"
      • skin 皮肤,默认为"",其实就是给dialog添加一个你的类名,方便重置样式
      • btns 按钮组,默认为['确认'],可选['xx',‘xx’],只取前两个作为有效值,第一个为confirm,第二个为cancel
      • confirm 点击confirm按钮的回调,如确认
      • cancel 点击cancel按钮的回调,如取消
      • shadeClose 是否开启点击遮罩关闭,默认true
      • animation 过渡动画,默认为1,可选0和2,注意这里的动画是指内容区的动画,最外层是固定为0
  • hide() 关闭dialog

过渡动画
用css3的animation,具体看下面的css

先写好布局、样式(后面会移植到Javascript生成DOM)

html

<!-- 最外层 -->
<div class="dialog-wrapper">
  <!-- 居中主要层 -->
  <div class="dialog">
    <!-- 标题 -->
    <div class="title">消息提示</div>
    <!-- 主要内容 -->
    <div class="content">兄弟,你好像忘记传content的值了</div>
    <!-- 按钮组 -->
    <div class="buttons">
      <div class="btn cancel-btn">取消</div>
      <div class="btn confirm-btn">确认</div>
    </div>
  </div>
</div>

css

body,
html {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}

.dialog-wrapper {
  position: fixed;
  display: flex;
  justify-content: center;
  align-items: center;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(49, 49, 49, 0.5);
  color: #313131;
  font-size: 10px;
  -webkit-tap-highlight-color: transparent;
}

.dialog-wrapper.fadeIn {
  animation: fadeIn .2s ease;
}

.dialog-wrapper.fadeOut {
  animation: fadeOut .2s ease forwards;
}

.dialog-wrapper .dialog {
  position: relative;
  width: 85vw;
  max-width: 30em;
  border-radius: .4em;
  background-color: #fff;
  box-sizing: border-box;
  overflow: hidden;
  box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.1);
}

.dialog-wrapper .dialog.slideDown {
  animation: slideDown .2s ease;
}

.dialog-wrapper .dialog.slideUp {
  animation: slideUp .2s ease forwards;
}

.dialog-wrapper .dialog.scaleIn {
  animation: scaleIn 0.2s cubic-bezier(0.07, 0.89, 0.95, 1.4);
}

.dialog-wrapper .dialog.scaleOut {
  animation: scaleOut 0.2s cubic-bezier(0.07, 0.89, 0.95, 1.4) forwards;
}

.dialog-wrapper .dialog .btn {
  cursor: pointer;
}

.dialog-wrapper .dialog .btn:active {
  background-color: #f4f4f4;
}

.dialog-wrapper .dialog .close-btn {
  position: absolute;
  top: 0;
  right: 0;
  padding: 10px;
  font-size: 1.8em;
}

.dialog-wrapper .dialog .title {
  font-size: 1.8em;
  padding: 15px;
  text-align: center;
  background-color: #f4f4f4;
}

.dialog-wrapper .dialog .title:empty {
  display: none;
}

.dialog-wrapper .dialog .content {
  padding: 40px 20px;
  font-size: 1.6em;
  text-align: center;
}

.dialog-wrapper .dialog .buttons {
  font-size: 1.6em;
  display: flex;
  flex-flow: row-reverse;
}

.dialog-wrapper .dialog .buttons .btn {
  flex: 1;
  padding: 15px;
  text-align: center;
  border-top: 1px solid #ebebeb;
}

.dialog-wrapper .dialog .buttons .btn.confirm-btn {
  color: #f2d985;
}

.dialog-wrapper .dialog .buttons .btn.cancel-btn {
  color: #313131;
  border-right: 1px solid #ebebeb;
}

@keyframes slideDown {
  from {
    transform: translateY(-3em);
  }
  to {
    transform: translateY(0);
  }
}

@keyframes slideUp {
  from {
    transform: translateY(0);
  }
  to {
    transform: translateY(-3em);
  }
}

@keyframes fadeIn {
  from {
    opacity: .5;
  }
  to {
    opacity: 1;
  }
}

@keyframes fadeOut {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

@keyframes scaleIn {
  from {
    transform: scale(0.8);
  }
  to {
    transform: scale(1);
  }
}

@keyframes scaleOut {
  from {
    transform: scale(1);
  }
  to {
    transform: scale(0.8);
  }
}

Javascript封装

第一步,基本搭建
使用立即执行函数,只对外暴露两个方法,show和hide

let dialog = (function () {

  // 节点类型
  let elem, dialog, cancelBtn, confirmBtn;

  // 动画函数数组
  let animaArr = new Array(['fadeIn', 'fadeOut'], ['slideDown', 'slideUp'], ['scaleIn', 'scaleOut']);

  // 当前动画类型
  let currAnimation = '';

  /**
   * @method getNeedElement 获取所需要的节点
   */
  let getNeedElement = function () {

  }

  /**
   * @method show 显示dialog组件
   * @param {Object} options 一系列参数
   * @returns {Object} 当前dialog节点
   */
  let show = function (options) {

  }

  /**
   * @method hide 关闭dialog组件
   */
  let hide = function (index) {

  }

  /**
   * @method bindEvent 给dialog绑定事件
   * @param {Object} confirm 确认回调
   * @param {Object} cancel 取消回调
   */
  let bindEvent = function (confirm, cancel, shadeClose) {

  }

  return {
    show,
    hide
  }

})();

第二步,编写show方法

let show = function (options = {}) {

  // 默认参数
  let {
    title = '', content = '兄弟,你好像忘记传content值了',
      skin = '', btns = ['确定'],
      confirm = null,
      cancel = null,
      shadeClose = true,
      animation = 1
  } = options;

  // 皮肤类名
  let skinClass = skin ? ` ${skin}` : '';

  // 给当前动画类型赋值
  currAnimation = animation;

  // 生成按钮
  let btnTemp = '';
  btns.forEach((item, index) => {
    if (index == 2) return;
    let btnClass = index == 0 ? 'confirm-btn' : 'cancel-btn';
    let temp = `<div class="btn ${btnClass}">${item}</div>`
    btnTemp += temp
  })

  // 最终生成的HTML
  let html = `
    <div class="dialog-wrapper fadeIn">
      <div class="dialog${skinClass} ${animaArr[currAnimation][0]}">
        <div class="title">${title}</div>
        <div class="content">${content}</div>
        <div class="buttons">${btnTemp}</div>
      </div>
    </div>
  `;

  // 添加到Body
  document.body.innerHTML += html;
  // 获取所需要的节点
  getNeedElement();
  // 绑定事件
  bindEvent(confirm, cancel, shadeClose);
  return elem;

}

第三步,编写hide方法

// 最外层加类名hide
let hide = function () {

  // 最外层执行显示动画(固定)
  elem.classList.add('fadeOut');
  // 内容层执行关闭动画
  dialog.classList.add(`${animaArr[currAnimation][1]}`);
  // 最终移除
  setTimeout(() => {
    elem.remove();
  }, 200);

}

第四步,编写bindEvent方法

let bindEvent = function (confirm, cancel) {

 // confirm按钮的回调
  confirmBtn && confirmBtn.addEventListener('click', e => {
    hide();
    confirm && confirm();
  })

  // cancel按钮的回调
  cancelBtn && cancelBtn.addEventListener('click', e => {
    hide();
    cancel && cancel();
  })

  // 是否开启点击遮罩关闭
  if (shadeClose) {
    elem.addEventListener('click', e => {
      let target = e.target || e.srcElement;
      if (/dialog-wrapper/.test(target.className)) {
        hide();
      }
    })
  }

}

第五步,编写getNeedElement方法

let getNeedElement = function () {
  
  // 一家人最重要是整整齐齐
  elem = document.querySelector('.dialog-wrapper');
  dialog = elem.querySelector('.dialog');
  cancelBtn = elem.querySelector('.cancel-btn');
  confirmBtn = elem.querySelector('.confirm-btn');

}

调用以及效果图

  1. 无标题


dialog.show({
  content: '抱歉,该游戏暂无Android版本'
})
  1. 自定义标题和按钮


dialog.show({
  title: '版本更新',
  content: '检测到最新版本为V1.0.2,是否更新',
  btns: ['立即更新', '暂不更新']
})
  1. 动画效果为2时(弹性放大)


 dialog.show({
  title: '消息提示',
  content: '此操作将不可逆转,确定删除此项?',
  btns: ['确定', '怕了'],
  animation: 2
})

说明

自己可以扩展更多自定义参数,动画需配合css3的animation

最后

本文到此结束,希望以上内容对你有些许帮助,如若喜欢请记得点个关注哦 💨

image

微信公众号「前端宇宙情报局」,将不定时更新最新、实用的前端技巧/技术性文章,欢迎关注,一起学习 🌘

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
    SunnyLeong阅读 14,608评论 1 180
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,373评论 0 17
  • 星期天的中午一时,我和妈妈在盐湖区解放路立交桥的桥墩旁的墙上,发现了一排爬山虎。爬山虎是一种植物,它的生存条件是:...
    孟浩东阅读 1,370评论 1 2
  • 简介:安福泸水河省级湿地公园位于横龙镇石溪村何家至平都镇泸水河铁路桥的约10公里水域及其周边草本沼泽湿地和部分林地...
    江西安福伊伊秋水阅读 1,136评论 0 1
  • 很多男人都有猎奇心理,美女总是耀眼的。 人都喜欢美好得事物,这没错,对于美女持欣赏态度,我是认可的。毕...
    初秋淡夏阅读 784评论 3 2