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



这一次主要是碰到了一个问题:极短时间内多次按Enter键触发”发送内容不能为空“的提示,提示也会多次触发渐隐效果,但实际上应该是出发一次,后来发现setTimeout()方法是有一个类似id的返回值(setInterval()方法也类似),可以用clearTimeout(id),将其停止。
下面给出代码:
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 /*页面公共样式*/
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 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 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 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 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 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 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- 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 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 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 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 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 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 80px;
275 height: 80px;
276 vertical-align: middle;
277 }
