實現一個聯繫客服對話框的前端部分

HTML 軟件 WebApp jQuery 科技優家 2017-05-19

一直都想寫一個對話框,正好公司買了一個,就照著外觀自己也寫一個,每次寫都會碰到意想不到的情況,通過解決這些情況,就很好的瞭解和學習了js知識。 先給出效果圖:

實現一個聯繫客服對話框的前端部分實現一個聯繫客服對話框的前端部分實現一個聯繫客服對話框的前端部分

這一次主要是碰到了一個問題:極短時間內多次按Enter鍵觸發”發送內容不能為空“的提示,提示也會多次觸發漸隱效果,但實際上應該是出發一次,後來發現setTimeout方法是有一個類似id的返回值(setInterval()方法也類似),可以用clearTimeout(id),將其停止。 同事,也測試了一下鍵盤事件的發生順序和可以獲得的內容。輸入框觸發事件的順序是focus-keydown-input-keyup-change-blur,在keydown發生的時候,能獲得keycode,但是不能獲得value;在input發生的時候,能獲得value;在keyup發生的時候,能獲得keycode,同時也能獲得value,就是利用這個,實現了Enter鍵發送消息,shift+Enter換行。

下面給出代碼:

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     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 }

相關推薦

推薦中...