html的原生自定义键盘(数字版)

简介: 前端现在很多时候,由于要限制文本的输入格式(这里指只允许输入数字),常常需要使用到自定义键盘。自定义键盘难免涉及到复用,资源占用等问题,有时候还会由于封装不好导致事件混乱、或者由于动画效果没实现好导致看上去很尴尬。

前言:


前端现在很多时候,由于要限制文本的输入格式(这里指只允许输入数字),常常需要使用到自定义键盘。自定义键盘难免涉及到复用,资源占用等问题,有时候还会由于封装不好导致事件混乱、或者由于动画效果没实现好导致看上去很尴尬。。。等这个那个的。本人根据这些情况,小做了一个原生的数字版键盘,希望可以给大家一点灵感,代码不好之处,敬请谅解!!!

正文:


正文,也叫代码解析,是程序员学习最快最有效的方式。。。。。

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>

运行结果:


2962e0b22fa2fb23f4dc2466adb086e3d0ce135e


后语:


  对了,我这个键盘放在body下的,所以body应该是高度100%,宽度充满,并且又overflow:hidden的,这部分观众老爷自行调整,position改fixed也是可以的
目录
相关文章
|
4月前
|
JavaScript
原生JS修改html内容不影响绑定的点击事件 请认准insertAdjacentHTML、insertAdjacentText方法
原生JS修改html内容不影响绑定的点击事件 请认准insertAdjacentHTML、insertAdjacentText方法
原生JS修改html内容不影响绑定的点击事件 请认准insertAdjacentHTML、insertAdjacentText方法
|
2月前
|
JavaScript
Vue中嵌入原生HTML页面的方法
Vue中嵌入原生HTML页面的方法
97 0
|
9月前
|
JavaScript 前端开发
html+原生js制作一个简易音乐播放器
html+原生js制作一个简易音乐播放器
118 0
|
4月前
|
存储 搜索推荐 前端开发
SpringBoot框架+原生HTML云端SaaS服务方式的电子病历编辑器源码
一体化电子病历系统基于云端SaaS服务的方式,采用B/S(Browser/Server)架构提供,覆盖了医疗机构电子病历模板制作到管理使用的整个流程。除实现在线制作内容丰富、图文并茂、功能完善的电子病历模板外,还可按照医疗机构的特色,根据不同业务的需求,使用该系统定制个性化、实用化、特色化电子病历模板。 该系统能对电子病历模板进行统一集中管理,通用的电子病历模板能直接提供给不同的医疗机构直接使用,避免重复制作;提供了功能齐备的控件元素,实现电子病历在业务使用中数据的自动获取功能,简化了人工的大量填报。
|
4月前
|
存储 前端开发 JavaScript
基于前端技术原生HTML、JS、CSS 电子病历编辑器源码
基于前端技术原生HTML、JS、CSS 电子病历编辑器源码
72 0
|
4月前
|
XML 前端开发 JavaScript
【原生HTML+SpringBoot】电子病历编辑器源码
【原生HTML+SpringBoot】电子病历编辑器源码
105 0
|
4月前
|
JavaScript
原生js插入HTML元素
原生js插入HTML元素
51 1
|
6月前
|
数据采集 数据安全/隐私保护
采用SpringBoot+原生HTML+MySQL开发的电子病历系统源码
本套电子病历系统主要面向医疗机构医生、护士,提供对住院病人的电子病历书写、保存、修改、打印等功能。本系统基于云端SaaS服务方式,通过浏览器方式访问和使用系统功能,提供电子病历在线制作、管理和使用的一体化电子病历解决方案,为医疗机构的电子病历业务开展提供支撑。
|
9月前
|
前端开发 JavaScript API
原生html实现一个mini-react-router
原生html实现一个mini-react-router 前言 实现一个简单版本的react-router, 揭秘路由的神秘面纱 思考 • 前端路由本质上是什么
159 0