前言:
前端现在很多时候,由于要限制文本的输入格式(这里指只允许输入数字),常常需要使用到自定义键盘。自定义键盘难免涉及到复用,资源占用等问题,有时候还会由于封装不好导致事件混乱、或者由于动画效果没实现好导致看上去很尴尬。。。等这个那个的。本人根据这些情况,小做了一个原生的数字版键盘,希望可以给大家一点灵感,代码不好之处,敬请谅解!!!
正文:
正文,也叫代码解析,是程序员学习最快最有效的方式。。。。。
keyBoard.js(封装的方法就在这里了,纯原生,不依赖,当然,IE9+哈,建议只扩展,不修改。当然,我写的不好的地方随便改,改了留个言帮我纠正一下。android后退键关闭的话,这里是没写的,需要cordova之类的插件支持才行)
(function(window, storage, undefined) {
'use strict'
window.keyBoard = function() {
var keyBoardDiv, keyBoard, commit, dialog, input, label, span, table, tbody, tr, td;
var keyBoardClick, keyBoardDivClick, keyBoardTranstionEnd;
var body = document.getElementsByTagName("body")[0];
var keyModels = {
SIMPLE: {
COLS: 3,
WIDTH: '33.3%',
TYPE: 1,
KEYS: [7, 8, 9, 4, 5, 6, 1, 2, 3, '-', 0, '<']
},
PLUS: {
COLS: 4,
WIDTH: '25%',
TYPE: 1,
KEYS: [7, 8, 9, 'C', 4, 5, 6, '↑', 1, 2, 3, '↓', '-', 0, '.', '<']
}
};
var transtion;
var currModel;
var closeCB;
var inputText = "",
currText, fixed = 0,
offset = -1;
var popEvent = function() {
this.closeKeyBoard(true);
};
var statusUtils = function() {
var changing = false;
return {
setChanging: function(status) {
changing = status;
},
getChanging: function() {
return changing;
}
}
}();
return {
openKeyBoard: openKeyBoard,
closeKeyBoard: closeKeyBoard,
keyModels: keyModels
};
function openKeyBoard(notice, initNumber, model, callbackEvery, callbackLast, openCallback, closeCallback) {
if(statusUtils.getChanging()) {
return false;
}
statusUtils.setChanging(true);
var _this = this;
/***** 处理返回事件 *******/
if(window.history && window.history.pushState) {
window.history.pushState(null, null, document.URL);
window.addEventListener("popstate", popEvent.bind(_this), false);
}
/***** 处理返回事件结束 *******/
// 参数置换
if(typeof model === "function") {
closeCallback = openCallback;
openCallback = callbackLast;
callbackLast = callbackEvery;
callbackEvery = model;
model = undefined;
}
// 关闭事件回调赋值
closeCB = closeCallback;
// UI
model = model || keyModels.SIMPLE;
if(!keyBoardDiv || model !== currModel) {
inputText = "";
currModel = model;
if(keyBoardDiv) {
body.removeChild(keyBoardDiv);
}
// 键盘上的对话框
dialog = document.createElement("DIV");
label = document.createElement("DIV");
span = document.createElement("SPAN");
input = document.createElement("SPAN");
commit = document.createElement("BUTTON");
dialog.className = 'qs-keyBoard-dialog';
commit.innerHTML = "完成";
input.className = "qs-inset-input";
input.style.textAlign = 'center';
label.appendChild(input);
label.appendChild(commit);
dialog.appendChild(span);
dialog.appendChild(label);
keyBoardDiv = document.createElement("DIV");
keyBoardDiv.className = "qs-key-board-bg";
// 键盘部分
keyBoard = document.createElement("DIV");
table = document.createElement("TABLE");
tbody = document.createElement("TBODY");
keyBoard.className = "qs-key-board";
keyBoard.id = 'qs-keyboard-id';
table.border = '0';
for(var i = 0; i < currModel.KEYS.length; i++) {
if(i % currModel.COLS === 0) {
tr = document.createElement("TR");
}
if(currModel.KEYS[i] || currModel.KEYS[i] === 0) {
td = document.createElement("TD");
td.style.width = currModel.WIDTH;
if(typeof(currModel.KEYS[i]) === "object") {
currModel.KEYS[i].icon ? td.className = currModel.KEYS[i].icon : td.innerHTML = currModel.KEYS[i].text;
currModel.KEYS[i].rows && td.setAttribute('rowspan', currModel.KEYS[i].rows);
td.setAttribute("qs-data-value", currModel.KEYS[i].text);
} else {
td.innerHTML = currModel.KEYS[i];
td.setAttribute("qs-data-value", currModel.KEYS[i]);
}
tr.appendChild(td);
}
if(i % currModel.COLS === currModel.COLS - 1) {
tbody.appendChild(tr);
}
}
table.appendChild(tbody);
keyBoard.appendChild(dialog);
keyBoard.appendChild(table);
keyBoardDiv.appendChild(keyBoard);
body.appendChild(keyBoardDiv);
}
input.innerHTML = inputText = (initNumber + "") || "";
span.innerHTML = notice || '';
//预移除事件(快速点击时动画误差)
transtion = whichTransitionEvent(keyBoardDiv);//判断当前使用的事件类型
if(keyBoardClick) {
keyBoard.removeEventListener("click", keyBoardClick);
keyBoardDiv.removeEventListener("click", keyBoardDivClick);
keyBoardDiv.removeEventListener(transtion, keyBoardTranstionEnd);
}
// 监听事件
keyBoardDivClick = function() {
inputText = inputText === '-' ? '' : inputText;
callbackLast && callbackLast(inputText ? Number(inputText) : '');
_this.closeKeyBoard();
};
keyBoardClick = function(e) {
switch(e.target.nodeName) {
case 'TD':
e.stopPropagation();
e.preventDefault();
doKeys(e);
break;
case 'BUTTON':
break;
default:
e.stopPropagation();
e.preventDefault();
break;
}
};
keyBoardTranstionEnd = function() {
statusUtils.setChanging(false);
openCallback && openCallback();
};
function doKeys(e) {
currText = e.target.getAttribute("qs-data-value");
inputText = inputText === '0' ? '' : inputText;
switch(currText) {
case '-':
inputText = inputText.indexOf('-') === -1 ? '-' + inputText : inputText.slice(1);
break;
case '.':
inputText = inputText ? inputText === '-' ? inputText = '-0.' : (inputText.indexOf('.') === -1 ? inputText + '.' : inputText) : '0.';
break;
case '<':
inputText = inputText ? inputText.slice(0, -1) : '';
break;
case 'C':
inputText = '';
break;
case '↑':
inputText = calcNumber(inputText, 2);
break;
case '↓':
inputText = calcNumber(inputText, 1);
break;
default:
inputText = inputText === '-0' ? '-' : inputText;
inputText += currText;
break;
}
input.innerHTML = inputText;
callbackEvery && callbackEvery(inputText ? Number(inputText) : '');
}
function calcNumber(str, type) {
str = str === '-' ? "0" : str;
offset = str.indexOf('.');
fixed = offset > -1 ? str.length - offset - 1 : 0;
str = Math.round(Number(str) * Math.pow(10, fixed) + Math.pow(10, fixed) * Math.pow(-1, type)) / Math.pow(10, fixed);
return str.toString();
}
// 注册监听事件
keyBoard.addEventListener("click", keyBoardClick, false);
keyBoardDiv.addEventListener("click", keyBoardDivClick, false);
keyBoardDiv.addEventListener(transtion, keyBoardTranstionEnd, false);
keyBoardDiv.className = "qs-key-board-bg";
setTimeout(function(){
keyBoardDiv.className = "qs-key-board-bg qs-keyboard-up";
});
}
/**
* 关闭键盘
* @param doBack 是否执行一次回退(不是导航栏返回触发的需要执行一次回退)
*/
function closeKeyBoard(doBack) {
if(statusUtils.getChanging()) {
return false;
}
statusUtils.setChanging(true);
// 动画完成事件
var closeKeyBoardTranstionEnd = function() {
keyBoardDiv.className = "qs-key-board-bg display-none";
statusUtils.setChanging(false);
keyBoardDiv.removeEventListener(transtion, closeKeyBoardTranstionEnd);
// 键盘关闭回调事件
closeCB && closeCB();
};
keyBoardDiv.addEventListener(transtion, closeKeyBoardTranstionEnd, false);
keyBoardDiv.className = "qs-key-board-bg";
inputText = '';
// 处理回退事件
if(window.history && window.history.pushState) {
if(!doBack) {
window.history.back();
}
window.removeEventListener("popstate", popEvent);
}
// 移除监听事件
keyBoard.removeEventListener("click", keyBoardClick);
keyBoardDiv.removeEventListener("click", keyBoardDivClick);
keyBoardDiv.removeEventListener(transtion, keyBoardTranstionEnd);
}
function whichTransitionEvent(el) {
var transitions = {
'transition': 'transitionend',
'OTransition': 'oTransitionEnd',
'MozTransition': 'transitionend',
'WebkitTransition': 'webkitTransitionEnd'
}
for(var t in transitions) {
if(el.style[t] !== undefined) {
return transitions[t];
}
}
}
}();
})(window, window.localStorage)
keyboard.css (这是键盘的样式文件,随便改)
.qs-key-board-bg {
position: fixed;
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;
}
keyboard.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title></title>
<link rel="stylesheet" href="css/keyBoard.css" />
<style>
html, body{
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
}
span#input{
display: inline-block;
border-bottom: solid 1px green;
width: 200px;
height: 30px;
line-height: 30px;
margin: 20px;
background-color: #EFEFEF;
}
</style>
</head>
<body>
<span id="input" onclick="openKeyBoard(this)"></span>
</body>
<script>
function openKeyBoard(_this){
keyBoard.openKeyBoard('请输入数字', _this.innerText, keyBoard.keyModels.PLUS, function(number){
_this.innerText = number;
});
}
</script>
<script type="text/javascript" src="js/keyBoard.js" ></script>
</html>
运行结果:
后语:
对了,我这个键盘放在body下的,所以body应该是高度100%,宽度充满,并且又overflow:hidden的,这部分观众老爷自行调整,position改fixed也是可以的