前言:
前端現在很多時候,由於要限制文本的輸入格式(這里指只允許輸入數字),常常需要使用到自定義鍵盤。自定義鍵盤難免涉及到復用,資源占用等問題,有時候還會由於封裝不好導致事件混亂、或者由於動畫效果沒實現好導致看上去很尷尬。。。等這個那個的。本人根據這些情況,小做了一個原生的數字版鍵盤,希望可以給大家一點靈感,代碼不好之處,敬請諒解!!!
正文:
正文,也叫代碼解析,是程序員學習最快最有效的方式。。。。。
keyBoard.js(封裝的方法就在這里了,純原生,不依賴,當然,IE9+哈,建議只擴展,不修改。當然,我寫的不好的地方隨便改,改了留個言幫我糾正一下。android后退鍵關閉的話,這里是沒寫的,需要cordova之類的插件支持才行)
1 (function(window, storage, undefined) { 2 'use strict' 3 4 window.keyBoard = function() { 5 var keyBoardDiv, keyBoard, commit, dialog, input, label, span, table, tbody, tr, td; 6 var keyBoardClick, keyBoardDivClick, keyBoardTranstionEnd; 7 var body = document.getElementsByTagName("body")[0]; 8 var keyModels = { 9 SIMPLE: { 10 COLS: 3, 11 WIDTH: '33.3%', 12 TYPE: 1, 13 KEYS: [7, 8, 9, 4, 5, 6, 1, 2, 3, '-', 0, '<'] 14 }, 15 PLUS: { 16 COLS: 4, 17 WIDTH: '25%', 18 TYPE: 1, 19 KEYS: [7, 8, 9, 'C', 4, 5, 6, '↑', 1, 2, 3, '↓', '-', 0, '.', '<'] 20 } 21 }; 22 23 var transtion; 24 var currModel; 25 var closeCB; 26 var inputText = "", 27 currText, fixed = 0, 28 offset = -1; 29 var popEvent = function() { 30 this.closeKeyBoard(true); 31 }; 32 var statusUtils = function() { 33 var changing = false; 34 return { 35 setChanging: function(status) { 36 changing = status; 37 }, 38 getChanging: function() { 39 return changing; 40 } 41 } 42 }(); 43 44 return { 45 openKeyBoard: openKeyBoard, 46 closeKeyBoard: closeKeyBoard, 47 keyModels: keyModels 48 }; 49 50 function openKeyBoard(notice, initNumber, model, callbackEvery, callbackLast, openCallback, closeCallback) { 51 if(statusUtils.getChanging()) { 52 return false; 53 } 54 statusUtils.setChanging(true); 55 var _this = this; 56 57 /***** 處理返回事件 *******/ 58 if(window.history && window.history.pushState) { 59 window.history.pushState(null, null, document.URL); 60 window.addEventListener("popstate", popEvent.bind(_this), false); 61 } 62 /***** 處理返回事件結束 *******/ 63 64 // 參數置換 65 if(typeof model === "function") { 66 closeCallback = openCallback; 67 openCallback = callbackLast; 68 callbackLast = callbackEvery; 69 callbackEvery = model; 70 model = undefined; 71 } 72 73 // 關閉事件回調賦值 74 closeCB = closeCallback; 75 76 // UI 77 model = model || keyModels.SIMPLE; 78 if(!keyBoardDiv || model !== currModel) { 79 inputText = ""; 80 currModel = model; 81 82 if(keyBoardDiv) { 83 body.removeChild(keyBoardDiv); 84 } 85 86 // 鍵盤上的對話框 87 dialog = document.createElement("DIV"); 88 label = document.createElement("DIV"); 89 span = document.createElement("SPAN"); 90 input = document.createElement("SPAN"); 91 commit = document.createElement("BUTTON"); 92 93 dialog.className = 'qs-keyBoard-dialog'; 94 commit.innerHTML = "完成"; 95 input.className = "qs-inset-input"; 96 input.style.textAlign = 'center'; 97 label.appendChild(input); 98 label.appendChild(commit); 99 dialog.appendChild(span); 100 dialog.appendChild(label); 101 102 keyBoardDiv = document.createElement("DIV"); 103 keyBoardDiv.className = "qs-key-board-bg"; 104 105 // 鍵盤部分 106 keyBoard = document.createElement("DIV"); 107 table = document.createElement("TABLE"); 108 tbody = document.createElement("TBODY"); 109 keyBoard.className = "qs-key-board"; 110 keyBoard.id = 'qs-keyboard-id'; 111 table.border = '0'; 112 for(var i = 0; i < currModel.KEYS.length; i++) { 113 if(i % currModel.COLS === 0) { 114 tr = document.createElement("TR"); 115 } 116 if(currModel.KEYS[i] || currModel.KEYS[i] === 0) { 117 td = document.createElement("TD"); 118 td.style.width = currModel.WIDTH; 119 if(typeof(currModel.KEYS[i]) === "object") { 120 currModel.KEYS[i].icon ? td.className = currModel.KEYS[i].icon : td.innerHTML = currModel.KEYS[i].text; 121 currModel.KEYS[i].rows && td.setAttribute('rowspan', currModel.KEYS[i].rows); 122 td.setAttribute("qs-data-value", currModel.KEYS[i].text); 123 } else { 124 td.innerHTML = currModel.KEYS[i]; 125 td.setAttribute("qs-data-value", currModel.KEYS[i]); 126 } 127 tr.appendChild(td); 128 } 129 if(i % currModel.COLS === currModel.COLS - 1) { 130 tbody.appendChild(tr); 131 } 132 } 133 table.appendChild(tbody); 134 keyBoard.appendChild(dialog); 135 keyBoard.appendChild(table); 136 keyBoardDiv.appendChild(keyBoard); 137 body.appendChild(keyBoardDiv); 138 } 139 140 input.innerHTML = inputText = (initNumber + "") || ""; 141 span.innerHTML = notice || ''; 142 143 //預移除事件(快速點擊時動畫誤差) 144 transtion = whichTransitionEvent(keyBoardDiv);//判斷當前使用的事件類型 145 if(keyBoardClick) { 146 keyBoard.removeEventListener("click", keyBoardClick); 147 keyBoardDiv.removeEventListener("click", keyBoardDivClick); 148 keyBoardDiv.removeEventListener(transtion, keyBoardTranstionEnd); 149 } 150 151 // 監聽事件 152 keyBoardDivClick = function() { 153 inputText = inputText === '-' ? '' : inputText; 154 callbackLast && callbackLast(inputText ? Number(inputText) : ''); 155 _this.closeKeyBoard(); 156 }; 157 158 keyBoardClick = function(e) { 159 switch(e.target.nodeName) { 160 case 'TD': 161 e.stopPropagation(); 162 e.preventDefault(); 163 doKeys(e); 164 break; 165 case 'BUTTON': 166 break; 167 default: 168 e.stopPropagation(); 169 e.preventDefault(); 170 break; 171 } 172 }; 173 174 keyBoardTranstionEnd = function() { 175 statusUtils.setChanging(false); 176 openCallback && openCallback(); 177 }; 178 179 function doKeys(e) { 180 currText = e.target.getAttribute("qs-data-value"); 181 inputText = inputText === '0' ? '' : inputText; 182 switch(currText) { 183 case '-': 184 inputText = inputText.indexOf('-') === -1 ? '-' + inputText : inputText.slice(1); 185 break; 186 case '.': 187 inputText = inputText ? inputText === '-' ? inputText = '-0.' : (inputText.indexOf('.') === -1 ? inputText + '.' : inputText) : '0.'; 188 break; 189 case '<': 190 inputText = inputText ? inputText.slice(0, -1) : ''; 191 break; 192 case 'C': 193 inputText = ''; 194 break; 195 case '↑': 196 inputText = calcNumber(inputText, 2); 197 break; 198 case '↓': 199 inputText = calcNumber(inputText, 1); 200 break; 201 default: 202 inputText = inputText === '-0' ? '-' : inputText; 203 inputText += currText; 204 break; 205 } 206 input.innerHTML = inputText; 207 callbackEvery && callbackEvery(inputText ? Number(inputText) : ''); 208 } 209 210 function calcNumber(str, type) { 211 str = str === '-' ? "0" : str; 212 offset = str.indexOf('.'); 213 fixed = offset > -1 ? str.length - offset - 1 : 0; 214 str = Math.round(Number(str) * Math.pow(10, fixed) + Math.pow(10, fixed) * Math.pow(-1, type)) / Math.pow(10, fixed); 215 return str.toString(); 216 } 217 218 // 注冊監聽事件 219 keyBoard.addEventListener("click", keyBoardClick, false); 220 keyBoardDiv.addEventListener("click", keyBoardDivClick, false); 221 keyBoardDiv.addEventListener(transtion, keyBoardTranstionEnd, false); 222 223 keyBoardDiv.className = "qs-key-board-bg"; 224 setTimeout(function(){ 225 keyBoardDiv.className = "qs-key-board-bg qs-keyboard-up"; 226 }); 227 } 228 229 /** 230 * 關閉鍵盤 231 * @param doBack 是否執行一次回退(不是導航欄返回觸發的需要執行一次回退) 232 */ 233 function closeKeyBoard(doBack) { 234 if(statusUtils.getChanging()) { 235 return false; 236 } 237 statusUtils.setChanging(true); 238 239 // 動畫完成事件 240 var closeKeyBoardTranstionEnd = function() { 241 keyBoardDiv.className = "qs-key-board-bg display-none"; 242 statusUtils.setChanging(false); 243 keyBoardDiv.removeEventListener(transtion, closeKeyBoardTranstionEnd); 244 245 // 鍵盤關閉回調事件 246 closeCB && closeCB(); 247 }; 248 keyBoardDiv.addEventListener(transtion, closeKeyBoardTranstionEnd, false); 249 keyBoardDiv.className = "qs-key-board-bg"; 250 inputText = ''; 251 252 // 處理回退事件 253 if(window.history && window.history.pushState) { 254 if(!doBack) { 255 window.history.back(); 256 } 257 window.removeEventListener("popstate", popEvent); 258 } 259 // 移除監聽事件 260 keyBoard.removeEventListener("click", keyBoardClick); 261 keyBoardDiv.removeEventListener("click", keyBoardDivClick); 262 keyBoardDiv.removeEventListener(transtion, keyBoardTranstionEnd); 263 } 264 265 function whichTransitionEvent(el) { 266 var transitions = { 267 'transition': 'transitionend', 268 'OTransition': 'oTransitionEnd', 269 'MozTransition': 'transitionend', 270 'WebkitTransition': 'webkitTransitionEnd' 271 } 272 273 for(var t in transitions) { 274 if(el.style[t] !== undefined) { 275 return transitions[t]; 276 } 277 } 278 } 279 }(); 280 })(window, window.localStorage)
keyboard.css (這是鍵盤的樣式文件,隨便改)
.qs-key-board-bg {
position: absolute;
pointer-events: painted;
width: 100%;
left: 0;
top: 100%;
height: 100%;
transition: top .3s ease;
-webkit-transition: top .3s ease;
z-index: 999;
-moz-user-select: none;
-ms-touch-select: none;
-ms-user-select: none;
-webkit-user-select: none;
}
.qs-key-board {
background-color: white;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}
.qs-keyBoard-dialog {
padding: 5px 10px;
background-color: white;
box-shadow: inset 0px 5px 15px #efefef;
}
.qs-keyBoard-dialog > div {
display: flex;
height: 30px;
}
.qs-keyBoard-dialog > div > button {
width: 6em;
}
.qs-keyBoard-dialog > span {
font-size: 14px;
display: block;
padding: 2px;
color: #999999;
white-space: nowrap;
text-overflow:ellipsis;
overflow:hidden;
}
.qs-key-board > table {
width: 100%;
background-color: #efefef;
border-spacing: 6px;
border-collapse: separate;
}
.qs-key-board tr{
height: 3.5rem;
}
.qs-key-board td {
width: 33.3%;
border: solid 1px #dedede;
border-radius: 6px;
-webkit-border-radius: 6px;
font-size: 2rem;
text-align: center;
vertical-align: middle;
background-color: white;
}
.qs-key-board td:active{
background-color: #dedede;
}
.qs-keyboard-up {
top: 0%;
}
.qs-inset-input {
position: relative;
display: inline-block;
border-radius: 3px;
-webkit-border-radius: 3px;
margin-right: 10px;
border: none;
font-size: 18px !important;
width: 100%;
height: 30px !important;
line-height: 30px;
background-color: rgb(238,238,238) !important;
}
.qs-keyboard-switch {
position: absolute;
overflow: hidden;
pointer-events: painted;
right: -120px;
z-index: 1000;
margin-bottom: -7px;
transition: right 300ms ease;
-webkit-transition: right 300ms ease;
}
.qs-keyboard-switch:before{
position: absolute;
z-index: 1;
right: 25px;
top:12px;
font-size: 20px;
color: white;
line-height: 20px;
width: 20px;
height: 20px;
}
.qs-keyboard-switch-show {
right: 0px;
}
.qs-input-dialog {
width: 100%;
z-index: 999;
position: absolute;
bottom: 0;
margin-bottom: -61px;
transition: margin-bottom 300ms ease;
}
.qs-input-dialog-show {
margin-bottom: 0px;
}
index.html(調用的例子,參考用)
1 <!DOCTYPE html> 2 <html> 3 4 <head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> 7 <title></title> 8 <link rel="stylesheet" href="css/keyBoard.css" /> 9 <style> 10 html, body{ 11 height: 100%; 12 padding: 0; 13 margin: 0; 14 overflow: hidden; 15 } 16 17 span#input{ 18 display: inline-block; 19 border-bottom: solid 1px green; 20 width: 200px; 21 height: 30px; 22 line-height: 30px; 23 margin: 20px; 24 background-color: #EFEFEF; 25 } 26 </style> 27 </head> 28 29 <body> 30 <span id="input" onclick="openKeyBoard(this)"></span> 31 </body> 32 <script> 33 function openKeyBoard(_this){ 34 keyBoard.openKeyBoard('請輸入數字', _this.innerText, keyBoard.keyModels.PLUS, function(number){ 35 _this.innerText = number; 36 }); 37 } 38 </script> 39 <script type="text/javascript" src="js/keyBoard.js" ></script> 40 </html>
后語:
對了,我這個鍵盤放在body下的,所以body應該是高度100%,寬度充滿,並且又overflow:hidden的,這部分觀眾老爺自行調整,position改fixed也是可以的