如何用原生js写一个弹窗消息提醒插件

1.分析

当消息被触发的时候,会有一个自上而下的淡入过程。
在持续了一段时间后会自动的消失,或者是需要用户来手动的点击关闭按钮。
在消息消失的时候,会有一个自下而上的淡出过程。
消息是可以叠加弹出的,最新的消息会排在消息列表的最后面。
当前面的消息消失后,后面的消息会有一个向上滑动效果。
然后消息本身是有三部分组成

消息图标,用来区分不同类型的消息。
消息文本。
关闭按钮,并不是所有消息都需要关闭按钮。

2. 实现样式

那么,不管我们是用原生js还是vue,首先呢,我们都需要把这个消息的基本样式给写出来,然后再通过js来控制消息的弹出和关闭。
所以,我们先来写html和css。

<!-- message.html -->
 <!-- 这个css是我引用阿里的一些字体图标,请戳: https://www.iconfont.cn/ -->
<link rel="stylesheet" href="http://at.alicdn.com/t/font_1117508_wxidm5ry7od.css">
<link rel="stylesheet" href="./message.css" rel="external nofollow" >
<script src="./message.js"></script>
 <!-- 消息外层容器,因为消息提醒基本上是全局的,所以这里用id,所有的弹出消息都是需要插入到这个容器里边的 -->
<div id="message-container">
 <div class="message">
 <!-- 消息图标 icon icon-success对应我的阿里字体图标的font-class -->
 <div class="type icon icon-success"></div>
 <!-- 消息文本 -->
 <div class="text">这是一条正经的消息~</div>
 <!-- 关闭按钮 -->
 <div class="close icon icon-close"></div>
 </div>
 <div class="message">
 <div class="type icon icon-error"></div>
 <div class="text">这是一条正经的消息~</div>
 </div>
</div>
/* message.css */
 #message-container {
 position: fixed;
 left: 0;
 top: 0;
 right: 0;
 /* 采用flex弹性布局,让容器内部的所有消息可以水平居中,还能任意的调整宽度 */
 display: flex;
 flex-direction: column;
 align-items: center;
}
#message-container .message {
 background: #fff;
 margin: 10px 0;
 padding: 0 10px;
 height: 40px;
 box-shadow: 0 0 10px 0 #eee;
 font-size: 14px;
 border-radius: 3px;
 /* 让消息内部的三个元素(图标、文本、关闭按钮)可以垂直水平居中 */
 display: flex;
 align-items: center;
}
#message-container .message .text {
 color: #333;
 padding: 0 20px 0 5px;
}
#message-container .message .close {
 cursor: pointer;
 color: #999;
}
 /* 给每个图标都加上不同的颜色,用来区分不同类型的消息 */
#message-container .message .icon-info {
 color: #0482f8;
}
#message-container .message .icon-error {
 color: #f83504;
}
#message-container .message .icon-success {
 color: #06a35a;
}
#message-container .message .icon-warning {
 color: #ceca07;
}
#message-container .message .icon-loading {
 color: #0482f8;
}

3. 实现动画

接下来要做的就是这个消息的弹出和消失动画,我们还是用css来实现。

想要在css里边实现自定义的动画,首先需要用@keyframes来定义一个动画规则,然后再通过animation属性把动画应用到某个元素上就可以了。
所谓的动画规则其实就是一个动画序列,或者可以理解为一个个的关键帧,而关键帧的内部就是你想改变的css属性,你可以在关键帧里边写上几乎任何的css属性,当动画被应用的时候,这些css属性就会根据各个关键帧做出相应的变换。

那我们先用@keyframes来写一个动画规则吧

/* message.css */
 /* 这个动画规则我们就叫做message-move-in吧,随后我们会用animation属性在某个元素上应用这个动画规则。 */
@keyframes message-move-in {
 0% {
 /* 前边分析过了,弹出动画是一个自上而下的淡入过程 */
 /* 所以在动画初始状态要把元素的不透明度设置为0,在动画结束的时候再把不透明度设置1,这样就会实现一个淡入动画 */
 opacity: 0;
 /* 那么“自上而下”这个动画可以用“transform”变换属性结合他的“translateY”上下平移函数来完成 */
 /* translateY(-100%)表示动画初始状态,元素在实际位置上面“自身一个高度”的位置。 */
 transform: translateY(-100%);
 }
 100% {
 opacity: 1;
 /* 平移到自身位置 */
 transform: translateY(0);
 }
}

然后我们再定义一个和message元素同级的类move-in,把message-move-in这个动画规则给应用到move-in类上,这样我们需要让哪个消息弹出,就只需要在消息的类上加一个move-in就行。

/* message.css */
 #message-container .message.move-in {
 /* animation属性是用来加载某个动画规则 请参考 https://developer.mozilla.org/zh-CN/docs/Web/CSS/animation */
 animation: message-move-in 0.3s ease-in-out;
}

可以看到,只需要在某个message上追加一个move-in就能实现弹出动画。
那么,消失动画也是一个套路,只不过跟弹出动画反过来而已。

/* message.css */
 @keyframes message-move-out {
 0% {
 opacity: 1;
 transform: translateY(0);
 }
 100% {
 opacity: 0;
 transform: translateY(-100%);
 }
}
 #message-container .message.move-out {
 animation: message-move-out 0.3s ease-in-out;
 /* 让动画结束后保持结束状态 */
 animation-fill-mode: forwards;
}

animation-fill-mode: forwards;这个是干嘛的呢?因为动画结束后默认会回到元素的最初状态,在这里表现的是消失后又出现了。

所以animation-fill-mode: forwards;是为了让动画结束后保持这个结束状态,也就是不在显示了。

4. 编写js插件

那么,在写js之前呢,我们先来思考一下,如果你是插件的使用者,你想怎么来调用这个插件?
我们的插件很简单,就是在需要的时候弹出一个消息,假设插件他提供给我们的是一个类,就叫做Message吧,并且他内部有一个show方法,那么只要使用者实例化这个类后,调用他的show方法,然后传入不同的参数就可以弹出一个消息了。而且我们所实例化的对象可以是全局唯一的。

<!-- message.html -->
<!-- 省略... -->
 <script>
// message可以定义为全局对象,项目中可以直接调用。
const message = new Message();
message.show({
 type: 'success',
 text: '点个关注不迷路~'
});
</script>

所以呢,我们要先写一个Message类,并且必须要实现一个show方法。

/* message.js */
 class Message {
 constructor() {
 }
 show({ type = 'info', text = '' }) {
 }
}

这里我直接用了es6的class关键词,其实他的内部还是原型链的形式。用class呢,可以让我们更直观的了解这个类。

根据我们在第一部分的分析,所有的消息元素都是需要在js中创建的,所以我们不需要使用者来写任何html代码,那么我们只需要在对象被实例化new Message()的时候,就去创建消息容器message-container,后续在调用show方法时候,直接把消息插入到message-container内部即可。

/* message.js */
 class Message {
 /**
 * 构造函数会在实例化的时候自动执行
 */
 constructor() {
 const containerId = 'message-container';
 // 检测下html中是否已经有这个message-container元素
 this.containerEl = document.getElementById(containerId);
 if (!this.containerEl) {
 // 创建一个Element对象,也就是创建一个id为message-container的dom节点
 this.containerEl = document.createElement('div');
 this.containerEl.id = containerId;
 // 把message-container元素放在html的body末尾
 document.body.appendChild(this.containerEl);
 }
 }
 show({ type = 'info', text = '' }) {
 }
}

这样,我们调用const message = new Message()的时候会在dom中自动的插入一个message-container节点。
那么,最重要的还是我们的show方法:

创建一个消息节点,并把它追加到message-container容器的末尾。
设定一个时间,在这个时间结束后自动的将消息移除。
监听“关闭按钮”的click事件,来让用户可以手动的移除消息。
我们一步一步来。

4.1 创建一个消息节点,并把它追加到message-container容器的末尾。

class Message {
 // 省略...
 show({ type = 'info', text = '' }) {
 // 创建一个Element对象
 let messageEl = document.createElement('div');
 // 设置消息class,这里加上move-in可以直接看到弹出效果
 messageEl.className = 'message move-in';
 // 消息内部html字符串
 messageEl.innerHTML = `
 <span class="icon icon-${type}"></span>
 <div class="text">${text}</div>
 <div class="close icon icon-close"></div>
 `;
 // 追加到message-container末尾
 // this.containerEl属性是我们在构造函数中创建的message-container容器
 this.containerEl.appendChild(messageEl);
 }

我们来调用下试试~

<!-- message.html -->
<!-- 省略... -->
 <button class="btn">弹窗消息提醒</button>
 <script>
 // message可以定义为全局对象,项目中可以直接调用。
 const message = new Message();
 document.querySelector('.btn').addEventListener('click', () => {
 message.show({
 type: 'success',
 text: '点个关注不迷路~'
 });
 });
 </script>

4.2 设定一个时间,在这个时间结束后自动的将消息移除。

// message.js
 class Message {
 // 省略...
 show({ type = 'info', text = '', duration = 2000 }) {
 // 省略...
 // 用setTimeout来做一个定时器
 setTimeout(() => {
 // Element对象内部有一个remove方法,调用之后可以将该元素从dom树种移除!
 messageEl.remove();
 }, duration);
 }
}

可以看到,消息在过了2秒后,自动的从dom树中移除了,不过呢并没有动画,还记得前边我们写了move-out类吗?这个类和message是同级的。现在我们只需要在定时结束后把这个类应用到message元素上就行。

// message.js
 class Message {
 // 省略...
 show({ type = 'info', text = '', duration = 2000 }) {
 // 省略...
 // 用setTimeout来做一个定时器
 setTimeout(() => { // 首先把move-in这个弹出动画类给移除掉,要不然会有问题,可以自己测试下
 messageEl.className = messageEl.className.replace('move-in', ''); // 增加一个move-out类
 messageEl.className += 'move-out';
 // 这个地方是监听动画结束事件,在动画结束后把消息从dom树中移除。
 // 如果你是在增加move-out后直接调用messageEl.remove,那么你不会看到任何动画效果
 messageEl.addEventListener('animationend', () => {
 // Element对象内部有一个remove方法,调用之后可以将该元素从dom树种移除!
 messageEl.remove();
 });
 }, duration);
 }
}

4.3 监听“关闭按钮”的click事件,来让用户可以手动的移除消息。

有时候呢,我们希望消息能够一直展示,直到用户来手动的关闭掉,那么首先我们要加一个参数,用来控制是否展示这个关闭按钮。

// message.js
 class Message {
 // 省略...
 show({ type = 'info', text = '', duration = 2000, closeable = false }) {
 // 创建一个Element对象
 let messageEl = document.createElement('div');
 // 设置消息class,这里加上move-in可以直接看到弹出效果
 messageEl.className = 'message move-in';
 // 消息内部html字符串
 messageEl.innerHTML = `
 <span class="icon icon-${type}"></span>
 <div class="text">${text}</div>
 `;
 // 是否展示关闭按钮
 if (closeable) {
 // 创建一个关闭按钮
 let closeEl = document.createElement('div');
 closeEl.className = 'close icon icon-close';
 // 把关闭按钮追加到message元素末尾
 messageEl.appendChild(closeEl);
 // 监听关闭按钮的click事件,触发后将调用我们的close方法
 // 我们把刚才写的移除消息封装为一个close方法
 closeEl.addEventListener('click', () => {
 this.close(messageEl)
 });
 }
 // 追加到message-container末尾
 // this.containerEl属性是我们在构造函数中创建的message-container容器
 this.containerEl.appendChild(messageEl);
 // 只有当duration大于0的时候才设置定时器,这样我们的消息就会一直显示
 if (duration > 0) {
 // 用setTimeout来做一个定时器
 setTimeout(() => {
 this.close(messageEl);
 }, duration);
 }
 }
 /**
 * 关闭某个消息
 * 由于定时器里边要移除消息,然后用户手动关闭事件也要移除消息,所以我们直接把移除消息提取出来封装成一个方法
 * @param {Element} messageEl
 */
 close(messageEl) {
 // 首先把move-in这个弹出动画类给移除掉,要不然会有问题,可以自己测试下
 messageEl.className = messageEl.className.replace('move-in', '');
 // 增加一个move-out类
 messageEl.className += 'move-out';
 // 这个地方是监听动画结束事件,在动画结束后把消息从dom树中移除。
 // 如果你是在增加move-out后直接调用messageEl.remove,那么你不会看到任何动画效果
 messageEl.addEventListener('animationend', () => {
 // Element对象内部有一个remove方法,调用之后可以将该元素从dom树种移除!
 messageEl.remove();
 });
 }
}

我们来调用下试试~

<!-- message.html -->
<!-- 省略... -->
 <button class="btn">弹窗消息提醒</button>
 <script>
 // message可以定义为全局对象,项目中可以直接调用。
 const message = new Message();
 document.querySelector('.btn').addEventListener('click', () => {
 message.show({
 type: 'warning',
 text: '点我旁边的叉叉试试',
 duration: 0, // 不会自动消失
 closeable: true, // 可手动关闭
 });
 });
 </script>

其实已经写的差不多了,不过还是有一些小问题,比如当我们弹出两个甚至更多消息的时候,如果前边的消息消失后,下面的消息会直接跳到上面的位置,很僵硬,没有任何的滑动。

我们可以通过css的transition属性来让meesage的高度逐渐变小,这样下面的元素就会根据变化来逐渐上移。

/* message.css */
/* 省略... */
 #message-container .message {
 background: #fff;
 margin: 10px 0;
 padding: 0 10px;
 height: 40px;
 box-shadow: 0 0 10px 0 #ccc;
 font-size: 14px;
 border-radius: 3px;
 /* 让消息内部的三个元素(图标、文本、关闭按钮)可以垂直水平居中 */
 display: flex;
 align-items: center;
/* 增加一个过渡属性,当message元素的高度和margin变化时候将会有一个过渡动画 */
 transition: height 0.2s ease-in-out, margin 0.2s ease-in-out;
}
/* 省略... */

然后我们只需要在Message类的close方法中做一下改变:

 close(messageEl) {
 // 首先把move-in这个弹出动画类给移除掉,要不然会有问题,可以自己测试下
 messageEl.className = messageEl.className.replace('move-in', '');
 // 增加一个move-out类
 messageEl.className += 'move-out';
 // move-out动画结束后把元素的高度和边距都设置为0
 // 由于我们在css中设置了transition属性,所以会有一个过渡动画
 messageEl.addEventListener('animationend', () => {
 messageEl.setAttribute('style', 'height: 0; margin: 0');
 });
 // 这个地方是监听transition的过渡动画结束事件,在动画结束后把消息从dom树中移除。
 messageEl.addEventListener('transitionend', () => {
 // Element对象内部有一个remove方法,调用之后可以将该元素从dom树种移除!
 messageEl.remove();
 });
 }

结尾
好了,基本上已经写好了,不过为了各个浏览器的兼容性,建议大家用babel转码,如果想发布,可以用webpack把js和css一块打包。
不过我们还是少考虑了一个场景,现在的关闭消息都是对象内部调用close方法来实现,如果我们希望外部能够控制消息的关闭呢,比如我请求服务器时候弹出一个loading的消息,现在服务器返回数据后,我怎么来关闭这个消息呢。
很简单,各位自行实现吧!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 纯js的右下角弹窗实例

    这个弹窗是如下图的效果: 打开网页的时候,这个弹窗会淡入,之后点击右上角的关闭按钮,其实就是一个& times;会淡出. 采用淡入淡出是因为直接Jquery一个fadeIn与fadeOut省事.如果采用窗口自下而上地移动,还要考虑div的position设置问题,这个问题还涉及一系列兼容性问题,很严重. 之所以称之为纯js的右下角弹窗,是因为,在任意页面,只需要如下引入Jquery之后,再引入这个Js,则可以使用,唯一注意就是Jquery的引入一定要在这个Js之前,由于我的JS是全基于Jquer

  • js用类封装pop弹窗组件

    下面的弹出框组件使用的是类来封装的.一个弹窗组件通过new一个实例来生成. 下面直接上代码: html结构: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> /*基本的样式*/ button{width: 1.6rem;height: 0.5rem;font-

  • Javascript中弹窗confirm与prompt的区别

    confirm 使用confirm,浏览器可以弹出一个确认框. 使用确认消息框可向用户问一个"是-或-否"问题,并且用户可以选择单击"确定"按钮或者单击"取消"按钮.confirm 方法的返回值为 true 或 false.该消息框也是模式对话框:用户必须在响应该对话框(单击一个按钮)将其关闭后,才能进行下一步操作. prompt  提示消息框提供了一个文本字段,用户可以在此字段输入一个答案来响应您的提示.该消息框有一个"确定"

  • JS实现自定义弹窗功能

    众所周知,浏览器自带的原生弹窗很不美观,而且功能比较单一,绝大部分时候我们都会按照设计图自定义弹窗或者直接使用注入layer的弹窗等等.前段时间在 慕课网 上看到了一个自定义弹窗的实现,自己顺便就学习尝试写了下,下面是主要的实现代码并添加了比较详细的注释,分享出来供大家参考.(代码用了ES6部分写法如需兼容低版本浏览器请把相关代码转成es5写法,后面有时间更新为一个兼容性较好的es5版本) HTML部分:(没什么内容 放置一个按钮调用函数,js中调用实例即可供参考) <!DOCTYPE html

  • 如何用原生js写一个弹窗消息提醒插件

    1.分析 当消息被触发的时候,会有一个自上而下的淡入过程. 在持续了一段时间后会自动的消失,或者是需要用户来手动的点击关闭按钮. 在消息消失的时候,会有一个自下而上的淡出过程. 消息是可以叠加弹出的,最新的消息会排在消息列表的最后面. 当前面的消息消失后,后面的消息会有一个向上滑动效果. 然后消息本身是有三部分组成 消息图标,用来区分不同类型的消息. 消息文本. 关闭按钮,并不是所有消息都需要关闭按钮. 2. 实现样式 那么,不管我们是用原生js还是vue,首先呢,我们都需要把这个消息的基本样式

  • 使用原生js写ajax实例(推荐)

    实例如下: // 使用原生js 封装ajax // 兼容xhr对象 function createXHR(){ if(typeof XMLHttpRequest != "undefined"){ // 非IE6浏览器 return new XMLHttpRequest(); }else if(typeof ActiveXObject != "undefined"){ // IE6浏览器 var version = [ "MSXML2.XMLHttp.6.0

  • 原生JS写Ajax的请求函数功能

    一般我们写网页的时候,如果用到 Ajax 请求服务器,都是使用 JQuery 等已经封装好的库来调用,比较简单. 但是一般这些库的功能很多,引入了太多我们用不到的东西,如果我们需要写一个功能单一,简单的页面,完全用不到引用如此庞大的库文件. 我们可以简单实现一个自己的 Ajax 请求功能,具体的代码如下: var ajax = {}; ajax.x = function () { if (typeof XMLHttpRequest !== 'undefined') { return new XM

  • 原生js添加一个或多个类名的方法分析

    本文实例讲述了原生js添加一个或多个类名的方法.分享给大家供大家参考,具体如下: 好吧今天写个js,不知道怎么添加类名了 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title></title> <style type="text/css" media="screen"

  • 用JS写一个发布订阅模式

    目录 1 场景引入 2 代码优化 2.1 解决增加粉丝问题 2.2 解决添加作品问题 3 观察者模式 4 经纪人登场 5 发布订阅模式 6 观察者模式和发布订阅模式的对比 什么是发布订阅模式?能手写实现一下吗?它和观察者模式有区别吗?... 1 场景引入 我们先来看这么一个场景: 假设现在有一个社交平台,平台上有一个大V叫Nami Nami很牛,多才多艺,目前她有2个技能:会写歌.会拍视频 她会把这些作品发布到平台上.关注她的粉丝就会接收到这些内容 现在他已经有3个粉丝了,分别是:Luffy.Z

  • 详解用Node.js写一个简单的命令行工具

    本文介绍了用Node.js写一个简单的命令行工具,分享给大家,具体如下: 操作系统需要为Linux 1. 目标 在命令行输入自己写的命令,完成目标任务 命令行要求全局有效 命令行要求可以删除 命令行作用,生成一个文件,显示当前的日期 2. 代码部分 新建一个文件,命名为sherryFile 文件sherryFile的内容 介绍: 生成一个文件,文件内容为当前日期和创建者 #! /usr/bin/env node console.log('command start'); const fs = r

  • 教你用Js写一个简单的五子棋小游戏

    目录 棋盘绘制 棋子的绘制 在点击 canvas 的时候获取相对于棋盘数据的坐标点 是否结束 悔棋功能 总结 这里的五子棋只做一些基础的功能,对于相对专业的规则不做处理. 那么该五子棋实现的规则和功能如下: 整体功能采用canvas实现 行列都规定 20 个数量,那么棋子的行列数量是 20 + 1 棋盘数据采用稀疏数组格式 棋子:0 为黑色,1 为白色 可以悔棋 胜负结束判断 棋盘绘制 <template> <div class="gobang"> <ca

  • TypeScript手写一个简单的eslint插件实例

    目录 引言 前置知识 第一个eslint规则:no-console 本地测试 本地查看效果 no-console规则添加功能:排除用户指定的文件 发布npm包 引言 看到参考链接1以后,觉得用TS写一个eslint插件应该很简单⌨️,尝试下来确实如此. 前置知识 本文假设 你对AST遍历有所了解. 你写过单测用例. 第一个eslint规则:no-console 为了简单,我们只使用tsc进行构建.首先package.json需要设置入口"main": "dist/index.

  • 使用Python写一个量化股票提醒系统

    大家在没有阅读本文之前先看下python的基本概念, Python是一种解释型.面向对象.动态数据类型的高级程序设计语言. Python由Guido van Rossum于1989年底发明,第一个公开发行版发行于1991年. 像Perl语言一样, Python 源代码同样遵循 GPL(GNU General Public License)协议. 本文是小兵使用万能的Python写一个量化股票系统!下面是一个小马的迷你量化系统. 这个小迷小量化系统,麻雀虽小但是五脏俱全,我们今天先从实时提醒这个模

  • 用 js 写一个 js 解释器过程详解

    用 js 来 编译 js 看起来是个高大上的东西,实际原理其实很简单,无非就是利用 js 对象属性可以用字符串表示 这个特性来实现的黑魔法罢了. 之所以看起来那么 深奥, 大概是由于网上现有的教程,都是动不动就先来个 babylon / @babel/parser 先让大家看个一大串的 AST, 然后再贴出一大串的代码, 直接递归 AST 处理所有类型的节点. 最后成功的把我这样的新手就被吓跑了. 那么今天我写这篇的目的,就是给大家一个浅显易懂,连刚学 js 的人都能看懂的 js2js 教程.

随机推荐