一直都想写一个对话框,正好公司买了一个,就照着外观自己也写一个,每次写都会碰到意想不到的情况,通过解决这些情况,就很好的了解和学习了js知识。
  先给出效果图:

      

    这一次主要是碰到了一个问题:极短时间内多次按Enter键触发”发送内容不能为空“的提示,提示也会多次触发渐隐效果,但实际上应该是出发一次,后来发现setTimeout()方法是有一个类似id的返回值(setInterval()方法也类似),可以用clearTimeout(id),将其停止。
    同时,也测试了一下键盘事件的发生顺序和可以获得的内容。输入框触发事件的顺序是focus-keydown-input-keyup-change-blur,在keydown发生的时候,能获得keycode,但是不能获得value;在input发生的时候,能获得value;在keyup发生的时候,能获得keycode,同时也能获得value,就是利用这个,实现了Enter键发送消息,shift+Enter换行。

    getElementByXX与querySelector(),querySelectorAll()还是有区别的,但是本文中getElementById()完全可以用后者替代。据我所知getElementById()性能上要比querySelector()快一些,而且getElementByXX是动态的查询,querySelectorAll()只能查询出HTML代码中的节点,不会考虑动态添加的。

    另外,操作DOM生成提问和回复的js代码写的不好,容我在搜索学习下=。=至少目前看来,createElement()之后应该马上appendChild()到父节点;可能一次性用innerHTML加入内容会更好。(2017年5月26日更新:今天在搜索关于回流与重绘的问题时候,突然想到了以前DOM笔记里的有关DocumentFragment类型的内容,毫无疑问,利用DocumentFragment类型来处理这种动态节点的添加再好不过了)

    下面给出代码:

    HTML & JS
  

  1 <!doctype html>
  2 <html>
  3 <head>
  4 <meta charset="utf-8">
  5 <title>客服聊天</title>
  6 <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
  7 <meta name="format-detection" content="telephone=no">
  8 <meta name="apple-mobile-web-app-capable" content="yes">
  9 <meta name="apple-mobile-web-app-status-bar-style" content="black">
 10 <link rel="stylesheet" href="styles/style.css">
 11 <script src="http://www.weizoom.com/static/resources/js/jquery-1.7.1.min.js"></script>
 12 </head>
 13 <body>
 14 <div class="dialogue-wrapper">
 15     <div id="btn_open" class="dialogue-support-btn">
 16         <i class="dialogue-support-icon"></i>
 17         <i class="dialogue-support-line"></i>
 18         <span class="dialogue-support-text">联系客服</span>
 19     </div>
 20     <div class="dialogue-main">
 21         <div class="dialogue-header">
 22             <i id="btn_close" class="dialogue-close">></i>
 23             <div class="dialogue-service-info">
 24                 <i class="dialogue-service-img">头像</i>
 25                 <div class="dialogue-service-title">
 26                     <p class="dialogue-service-name">XX客服</p>
 27                     <p class="dialogue-service-detail">XX客服支持平台</p>
 28                 </div>
 29             </div>
 30         </div>
 31         <div id="dialogue_contain" class="dialogue-contain">
 32             <p class="dialogue-service-contain"><span class="dialogue-text dialogue-service-text">您好,请提问</span></p>
 33             <!-- <p class="dialogue-customer-contain"><span class="dialogue-text dialogue-customer-text">我有个问题</span></p> -->
 34         </div>
 35         <div class="dialogue-submit">
 36             <p id="dialogue_hint" class="dialogue-hint"><span class="dialogue-hint-icon">!</span><span class="dialogue-hint-text">发送内容不能为空</span></p>
 37             <textarea id="dialogue_input" class="dialogue-input-text" placeholder="请输入您的问题,按Enter键提交(shift+Enter换行)"></textarea>
 38             <div class="dialogue-input-tools">
 39                 小工具预留位置
 40             </div>
 41         </div>
 42     </div>
 43 </div>
 44 <script>
 45     var doc = document;
 46     // 模拟一些后端传输数据
 47     var serviceData = {
 48         'robot': {
 49             'name': 'robot001',
 50             'dialogue': ['模拟回复1', '模拟回复2', '模拟回复3'],
 51             'welcome': '您好,robot001为您服务'
 52         }
 53     };
 54
 55     var dialogueInput = doc.getElementById('dialogue_input'),
 56         dialogueContain = doc.getElementById('dialogue_contain'),
 57         dialogueHint = doc.getElementById('dialogue_hint'),
 58         btnOpen = doc.getElementById('btn_open'),
 59         btnClose = doc.getElementById('btn_close'),
 60         timer,
 61         timerId,
 62         shiftKeyOn = false;  // 辅助判断shift键是否按住
 63
 64     btnOpen.addEventListener('click', function(e) {
 65         $('.dialogue-support-btn').css({'display': 'none'});
 66         $('.dialogue-main').css({'display': 'inline-block', 'height': '0'});
 67         $('.dialogue-main').animate({'height': '600px'})
 68     })
 69
 70     btnClose.addEventListener('click', function(e) {
 71         $('.dialogue-main').animate({'height': '0'}, function() {
 72             $('.dialogue-main').css({'display': 'none'});
 73             $('.dialogue-support-btn').css({'display': 'inline-block'});
 74         });
 75     })
 76
 77     dialogueInput.addEventListener('keydown', function(e) {
 78         var e = e || window.event;
 79         if (e.keyCode == 16) {
 80             shiftKeyOn = true;
 81         }
 82         if (shiftKeyOn) {
 83             return true;
 84         } else if (e.keyCode == 13 && dialogueInput.value == '') {
 85             // console.log('发送内容不能为空');
 86             // 多次触发只执行最后一次渐隐
 87             setTimeout(function() {
 88                 fadeIn(dialogueHint);
 89                 clearTimeout(timerId)
 90                 timer = setTimeout(function() {
 91                     fadeOut(dialogueHint)
 92                 }, 2000);
 93             }, 10);
 94             timerId = timer;
 95             return true;
 96         } else if (e.keyCode == 13) {
 97             var nodeP = doc.createElement('p'),
 98                 nodeSpan = doc.createElement('span');
 99             nodeP.classList.add('dialogue-customer-contain');
100             nodeSpan.classList.add('dialogue-text', 'dialogue-customer-text');
101             nodeSpan.innerHTML = dialogueInput.value;
102             nodeP.appendChild(nodeSpan);
103             dialogueContain.appendChild(nodeP);
104             dialogueContain.scrollTop = dialogueContain.scrollHeight;
105             submitCustomerText(dialogueInput.value);
106         }
107     });
108
109     dialogueInput.addEventListener('keyup', function(e) {
110         var e = e || window.event;
111         if (e.keyCode == 16) {
112             shiftKeyOn = false;
113             return true;
114         }
115         if (!shiftKeyOn && e.keyCode == 13) {
116             dialogueInput.value = null;
117         }
118     });
119
120     function submitCustomerText(text) {
121         console.log(text)
122         // code here 向后端发送text内容
123
124         // 模拟后端回复
125         var num = Math.random() * 10;
126         if (num <= 7) {
127             getServiceText(serviceData);
128         }
129     }
130
131     function getServiceText(data) {
132         var serviceText = data.robot.dialogue,
133             i = Math.floor(Math.random() * serviceText.length);
134         var nodeP = doc.createElement('p'),
135             nodeSpan = doc.createElement('span');
136         nodeP.classList.add('dialogue-service-contain');
137         nodeSpan.classList.add('dialogue-text', 'dialogue-service-text');
138         nodeSpan.innerHTML = serviceText[i];
139         nodeP.appendChild(nodeSpan);
140         dialogueContain.appendChild(nodeP);
141         dialogueContain.scrollTop = dialogueContain.scrollHeight;
142     }
143
144     // 渐隐
145     function fadeOut(obj) {
146         var n = 100;
147         var time = setInterval(function() {
148             if (n > 0) {
149                 n -= 10;
150                 obj.style.opacity = '0.' + n;
151             } else if (n <= 30) {
152                 obj.style.opacity = '0';
153                 clearInterval(time);
154             }
155         }, 10);
156         return true;
157     }
158
159     // 渐显
160     function fadeIn(obj) {
161         var n = 30;
162         var time = setInterval(function() {
163             if (n < 90) {
164                 n += 10;
165                 obj.style.opacity = '0.' + n;
166             } else if (n >= 80) {
167
168                 obj.style.opacity = '1';
169                 clearInterval(time);
170             }
171         }, 100);
172         return true;
173     }
174 </script>
175 </body>
176 </html>

    CSS

  

  1 @charset "utf-8";
  2 /*公共样式*/
  3 html{font-family:"Helvetica Neue",Helvetica,STHeiTi,sans-serif;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;}
  4 body{-webkit-overflow-scrolling:touch;margin:0;}
  5 ul{margin:0;padding:0;list-style:none;outline:none;}
  6 dl,dd{margin:0;}
  7 a{display:inline-block;margin:0;padding:0;text-decoration:none;background:transparent;outline:none;color:#000;}
  8 a:link,a:visited,a:hover,a:active{text-decoration:none;color:currentColor;}
  9 a,dt,dd{-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;}
 10 img{border:0;}
 11 p{margin:0;}
 12 input,button,select,textarea{margin:0;padding:0;border:0;outline:0;background-color:transparent;}
 13 /*css reset*/
 14 body {
 15     position: relative;
 16 }
 17
 18 .dialogue-wrapper {
 19     font-size: 14px;
 20     color: #fff;
 21 }
 22 /*右侧点击按钮*/
 23 .dialogue-wrapper .dialogue-support-btn {
 24     position: fixed;
 25     display: inline-block;
 26     top: 50%;
 27     right: 0;
 28     margin-top: -70px;
 29     padding: 10px 0;
 30     width: 40px;
 31     height: 120px;
 32     font-size: 16px;
 33     font-weight: 500;
 34     text-align: center;
 35     cursor: pointer;
 36     border-top-left-radius: 5px;
 37     border-bottom-left-radius: 5px;
 38     box-shadow: -1px 1px 5px rgba(0, 0, 0, .4);
 39     background-color: #5d94f3;
 40 }
 41
 42 .dialogue-wrapper .dialogue-support-btn .dialogue-support-icon {
 43     position: relative;
 44     display: inline-block;
 45     margin-bottom: -2px;
 46     width: 20px;
 47     height: 16px;
 48     border-radius: 4px;
 49     background-color: #fff;
 50 }
 51
 52 .dialogue-wrapper .dialogue-support-btn .dialogue-support-icon:before {
 53     content: '';
 54     position: absolute;
 55     left: 50%;
 56     bottom: -6px;
 57     margin-left: -3px;
 58     width: 0;
 59     height: 0;
 60     border-left: 4px solid transparent;
 61     border-right: 4px solid transparent;
 62     border-top: 6px solid #fff;
 63 }
 64
 65 .dialogue-wrapper .dialogue-support-btn .dialogue-support-line {
 66     display: inline-block;
 67     width: 100%;
 68     height: 1px;
 69     background-color: #ddd;
 70 }
 71
 72 .dialogue-wrapper .dialogue-support-btn .dialogue-support-text {
 73     padding: 5px 0;
 74     letter-spacing: 4px;
 75     writing-mode: vertical-rl;
 76     -webkit-user-select: none;
 77 }
 78
 79 /*底部客服对话框*/
 80 .dialogue-wrapper .dialogue-main {
 81     position: fixed;
 82     display: none;
 83     right: 100px;
 84     bottom: 10px;
 85     width: 400px;
 86     height: 600px;
 87     border-radius: 4px;
 88     box-shadow: 0 0 5px rgba(0, 0, 0, .4);
 89 }
 90
 91 /*客服对话框头部*/
 92 .dialogue-wrapper .dialogue-main .dialogue-header {
 93     position: relative;
 94     padding: 10px;
 95     height: 80px;
 96     border-top-left-radius: 4px;
 97     border-top-right-radius: 4px;
 98     box-shadow: 0 0 5px rgba(0, 0, 0, .2);
 99     background-color: #5d94f3;
100 }
101
102 .dialogue-wrapper .dialogue-main .dialogue-close {
103     position: absolute;
104     top: 10px;
105     right: 20px;
106     padding: 2px;
107     font-size: 22px;
108     transform: rotate(90deg);
109     cursor: pointer;
110 }
111
112 .dialogue-wrapper .dialogue-main .dialogue-service-info {
113     position: relative;
114     top: 50%;
115     margin-top: -20px;
116     height: 40px;
117 }
118
119 .dialogue-wrapper .dialogue-main .dialogue-service-img {
120     display: inline-block;
121     margin: 0 10px 0 20px;
122     width: 40px;
123     height: 40px;
124     text-align: center;
125     line-height: 40px;
126     vertical-align: middle;
127     color: #000;
128     border-radius: 50%;
129     box-shadow: 1px 1px 4px rgba(0, 0, 0, .2);
130     background-color: #fff;
131 }
132
133 .dialogue-wrapper .dialogue-main .dialogue-service-title {
134     display: inline-block;
135     vertical-align: middle;
136 }
137
138 .dialogue-wrapper .dialogue-main .dialogue-service-detail {
139     font-size: 12px;
140 }
141
142 /*客服对话框内容*/
143 .dialogue-wrapper .dialogue-main .dialogue-contain {
144     overflow-y: auto;
145     padding: 10px;
146     height: 380px;
147     word-wrap: break-word;
148     background-color: #f9f9f9;
149 }
150
151 .dialogue-wrapper .dialogue-main .dialogue-text {
152     display: inline-block;
153     position: relative;
154     padding: 10px;
155     max-width: 120px;
156     white-space: pre-wrap;
157     border: 1px solid #09d07d;
158     border-radius: 4px;
159     background-color: #09d07d;
160     box-sizing: border-box;
161 }
162
163 .dialogue-wrapper .dialogue-main .dialogue-service-contain {
164     margin-bottom: 10px;
165     text-align: left;
166 }
167
168 .dialogue-wrapper .dialogue-main .dialogue-service-text {
169     margin-left: 20px;
170 }
171
172 .dialogue-wrapper .dialogue-main .dialogue-service-text:before {
173     content: '';
174     position: absolute;
175     top: 50%;
176     left: -10px;
177     width: 0;
178     height: 0;
179     border-top: 6px solid transparent;
180     border-bottom: 6px solid transparent;
181     border-right: 10px solid #09d07d;
182     -webkit-transform: translate(0, -50%);
183     transform: translate(0, -50%);
184 }
185
186 .dialogue-wrapper .dialogue-main .dialogue-customer-contain {
187     margin-bottom: 10px;
188     text-align: right;
189 }
190
191 .dialogue-wrapper .dialogue-main .dialogue-customer-text {
192     margin-right: 20px;
193 }
194
195 .dialogue-wrapper .dialogue-main .dialogue-customer-text:after {
196     content: '';
197     position: absolute;
198     top: 50%;
199     right: -10px;
200     width: 0;
201     height: 0;
202     border-top: 6px solid transparent;
203     border-bottom: 6px solid transparent;
204     border-left: 10px solid #09d07d;
205     -webkit-transform: translate(0, -50%);
206     transform: translate(0, -50%);
207 }
208
209 /*客服对话框底部与输入*/
210 .dialogue-wrapper .dialogue-main .dialogue-submit {
211     position: relative;
212     padding: 10px;
213     height: 100px;
214     color: #000;
215     word-wrap: break-word;
216     border-top: 1px solid #ddd;
217     box-sizing: border-box;
218 }
219
220 /*空输入提示*/
221 .dialogue-wrapper .dialogue-main .dialogue-hint {
222     position: absolute;
223     top: -15px;
224     left: 20px;
225     padding: 2px;
226     width: 140px;
227     height: 18px;
228     opacity: 0;
229     font-size: 12px;
230     text-align: center;
231     line-height: 18px;
232     border: 1px solid #ddd;
233     box-shadow: 1px 1px 4px rgba(0, 0, 0, .4);
234     background-color: #fff;
235 }
236
237 .dialogue-wrapper .dialogue-main .dialogue-hint-icon {
238     display: inline-block;
239     width: 18px;
240     height: 18px;
241     margin-right: 5px;
242     font-size: 14px;
243     font-style: italic;
244     font-weight: 700;
245     vertical-align: middle;
246     line-height: 18px;
247     color: #fff;
248     border-radius: 50%;
249     background-color: #5d94f3
250 }
251
252 .dialogue-wrapper .dialogue-main .dialogue-hint-text {
253     display: inline-block;
254     vertical-align: middle;
255 }
256
257 /*输入框*/
258 .dialogue-wrapper .dialogue-submit .dialogue-input-text {
259     overflow-y: auto;
260     display: inline-block;
261     padding: 5px 10px;
262     width: 295px;
263     height: 70px;
264     vertical-align: middle;
265     white-space: pre-wrap;
266     word-wrap: break-word;
267     resize: none;
268     border-right: 1px solid #ddd;
269     box-sizing: border-box;
270 }
271
272 .dialogue-wrapper .dialogue-submit .dialogue-input-tools {
273     display: inline-block;
274     width: 80px;
275     height: 80px;
276     vertical-align: middle;
277 }

  2017年6月1日补充:

  利用端午好好夯实了一下js的对象、原型链、继承等知识。

  代码中的渐隐、渐显完全可以扩展到Object的原型对象上:

  

 1     // 渐隐
 2     Object.prototype.iFadeOut = function() {
 3         var n = 100,
 4             that = this;
 5         var time = setInterval(function() {
 6             if (n > 0) {
 7                 n -= 10;
 8                 that.style.opacity = '0.' + n;
 9             } else if (n <= 30) {
10                 that.style.opacity = '0';
11                 clearInterval(time);
12             }
13         }, 10);
14         return true;
15     }

  那么我们使用就看起来更为方便:node.iFadeOut()即可,例如dialogueHint.iFadeOut()。

转载于:https://www.cnblogs.com/youyouluo/p/6853436.html

实现一个联系客服对话框的前端部分相关推荐

  1. 一周开发一个轻量级客服系统(代码开源)

    文章目录 前言 一.聊天系统为什么使用短连接? 二.技术方案 后端技术方案: 前端技术方案 原生端 三.代码详细设计 1.数据库设计 2.后端程序 3.前端程序 四.效果展示 五.源码-GitHub ...

  2. 在线云客服管理系统、会话管理、访客管理、客户管理、工单管理、会话记录、考勤统计、数据报表、工单设置、全局设置、转人工服务、自动回复、客户标签、客服监控、客服系统、前端会话、客服管理、在线客服、人工客服

    在线云客服管理系统.会话管理.访客管理.客户管理.工单管理.会话记录.考勤统计.数据报表.工单设置.全局设置.转人工服务.自动回复.客户标签.客服监控.客服系统.前端会话.客服管理.在线客服 .人工客 ...

  3. 在线云客服管理系统、会话管理、访客管理、客户管理、工单管理、会话记录、考勤统计、数据报表、工单设置、全局设置、人工服务、自动回复、客户标签、客服监控、客服系统、前端会话、客服管理、在线客服 、人工客服

    在线云客服管理系统.会话管理.访客管理.客户管理.工单管理.会话记录.考勤统计.数据报表.工单设置.全局设置.转人工服务.自动回复.客户标签.客服监控.客服系统.前端会话.客服管理.在线客服 .人工客 ...

  4. 一个智能客服系统的设计思路

    一.目标 随着移动互联网发展,许多线下服务都搬到了网络平台上,人们也越来越习惯于通过互联网来获得服务,这大大节省了时间,提高了办事效率.但线上服务面向的用户群体大,同类问题多,这就要求互联网平台提高服 ...

  5. 设计一个智能客服系统

    背景: 最近在设计一个公司的智能客服系统,通过对现有人工客服语料作为样本,通过训练样本完成整个QA过程或业务办理过程. 整体思路 AliceBot负责闲聊,这里用了开源的语料,也可以添加语料到DB,基 ...

  6. 如何配置网页端联系客服对话框

    1.本次利用的是环云客服云,登录环信客服云. 2.设置对话框 <head><script>window.easemobim = window.easemobim || {};e ...

  7. 【node.js+html】无聊在家写一个在线客服聊天系统

    [系统概述] 使用Node.js+socket.io建立socket连接 编写独立客户端页面 编写独立客服进入页面 编写一个可以一(客服)对多(客户)的在线客户服务系统,实现同时接收多个咨询. [系统 ...

  8. 一个开源客服系统-wolive

    开源客服系统 wolive是一款基于thinkphp5开发的开源客服系统,即时消息基于开源框架thinkphp开发.支持PC浏览器和H5移动端,只需要插入一段html片段即可快速接入现有系统,支持与现 ...

  9. 客服对话框php代码,在线客服对话框

    摘要: &聊天室 .box1{width:320px;height:500px;box-shadow: 5px 5px 5px 0 grey;border-radius:5px;backgro ...

最新文章

  1. Hystrix 超时配置重写
  2. 运算放大器在超高频信号作用下的偏移量的变化
  3. python编程能干什么-Python编程一般可以用来做什么
  4. hdu 2196(树的最长链)
  5. DI是实现面向切面和面向抽象的前提
  6. Flutter MaterialApp概述以及主题配置概述
  7. 容器赋能AI-人工智能在360私有云容器服务上的实践
  8. MySQL 聚合函数(一)聚合(组合)函数概述
  9. python 多线程读写文件_Python多线程同步---文件读写控制方法
  10. QPSK信号在高斯信道、瑞利信道、Ricean信道下的误码率仿真(matlab)
  11. 矩孔菲涅尔衍射 matlab,圆孔矩孔的菲涅尔衍射模拟(matlab实现)-工程光学
  12. C++基于ATM的个人银行账户管理系统
  13. Alibaba微服务组件Sentinel
  14. 总结30个Python赚钱的接单平台!兼职月入5000+,成年人的世界,钱是活下去的筹码
  15. 翻译 | 正式发布Qt 5.11
  16. pytorch官方文档(中文版)
  17. 与苹果相反 三星向所有第三方应用开放指纹识别功能
  18. Java HashMap的put方法
  19. mysql 统计每年的数据统计_Mysql统计每年每个月的数据——详细教程
  20. 每日学习参考答案(1000-1021)

热门文章

  1. 跨境电商竞争格局头部化,中小平台如何突围?
  2. Linux驱动_i2c驱动(ap3216c)
  3. 电脑端用火狐浏览器访问wap站点
  4. 微信小程序properties属性有observe。依赖它的数据可以考虑在这里处理,不用单独写在observe里面
  5. 《最高人民法院最高人民检察院关于办理非法利用信息网络、帮助信息网络犯罪活动等刑事案件适用法律若干问题的解释》
  6. 山东省省外院校毕业生注册【山东省高校毕业生就业信息网】须知
  7. 暨南大学计算机研究生进腾讯,腾讯内容开放平台
  8. (2022版)一套教程搞定k8s安装到实战 | Ingress
  9. python多线程实现访问页面升级_python使用多线程不断刷新网页的方法
  10. python 实现神经网络 处理数据集cifar10