转自博文:格式化输入框内容后定位光标位置
具体计算规则
监听 keydown 事件,获取当前光标的位置。
根据输入框的内容和光标位置,计算出当前光标位置的前一个字符的下标(不计算空格)。
判断用户按了哪个键,是退格还是[0-9a-f]。如果是退格,光标应该定位在前一个字符,否则定位到后一个字符,从而计算出期望光标定位在哪个字符后面(不计算空格)。
格式化数据,根据字符位置计算出光标位置(计算空格)。
这里需要注意几点:
当进行选中删除操作时,光标应该不需要前移。
其他输入框不支持的字符需要处理,例如:ctrl等功能键。
html:
<div class="container">
<input type="text" id="input">
</div>
sass:
.container {
text-align: center;
padding-top: 46px;
}
#input {
font-variant: tabular-nums;
box-sizing: border-box;
margin: 0;
display: inline-block;
padding: 4px 11px;
width: 300px;
height: 32px;
font-size: 14px;
line-height: 1.5;
color: rgba(0, 0, 0, 0.65);
background-color: #fff;
border: 1px solid #d9d9d9;
border-radius: 4px;
transition: all .3s;
&:focus {
border-color: #40a9ff;
outline: 0;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
border-right-width: 1px !important;
}
}
babel:
const input = document.querySelector('#input');
let expectCharIndex; // 期望光标所处位置前一个字符的下标(不包括空格)
input.onkeydown = function(e) {
const curValue = input.value; // 当前值
console.log('keydown 阶段,此时 value 为:', curValue || '空');
const selectionStart = input.selectionStart; // 当前选中的区域起始位置
const selectionEnd = input.selectionEnd; // 当前选中区域结束位置
console.log('当前光标位置:', selectionStart);
const code = e.keyCode || e.which || e.charCode;
const preCharIndex = getPreviousCharIndex(curValue, selectionStart);
console.log('当前光标前一个字符的下标:', preCharIndex);
// 计算编辑之后光标前一个字符
if (code === 8) {
if (selectionStart !== selectionEnd) { // 选中区间删除,此时光标应该不前移动
expectCharIndex = preCharIndex;
} else {
expectCharIndex = preCharIndex - 1;
}
} else {
expectCharIndex = preCharIndex + 1;
}
console.log('格式化后,期望光标前一个字符的下标:', expectCharIndex);
};
input.onkeyup = function() {
const value = formatValue(input.value);
console.log('keyup 阶段,此时 value 为:', value);
input.value = value;
const cursorIndex = getCursorIndex(value, expectCharIndex);
console.log('期望光标的位置:', cursorIndex);
console.log('=============================');
// TODO 增加判断,如果是功能键等则不设置
setTimeout(() => {
input.setSelectionRange(cursorIndex, cursorIndex);
}, 10);
};
/**
* 格式化输入框内容,每两个字节中间插入一个空格。
*
* @param {string} value - 输入框内容
* @return {string}
*/
function formatValue(value) {
if (!value) {
return value;
}
value = value.replace(/\s*/g, '');
if (value.length <= 2) {
return value;
}
const arr = [];
for (let i = 0; i < value.length - 1; i += 2) {
arr.push(value.charAt(i) + value.charAt(i + 1));
}
if (value.length & 1) {
// 如果是奇数,还需要添加末尾的数
arr.push(value.charAt(value.length - 1));
}
return arr.join(' ');
}
/**
* 根据输入的内容和光标位置计算前一个字符的下标(不计算空格)
*
* @param {string} value - 格式化后的输入框内容
* @param {number} cursorIndex - 当前光标的位置
* @return {number} 光标前一个字符的下标,如果前面没有字符,则返回 -1。
*/
function getPreviousCharIndex(value, cursorIndex) {
let subStr = value.substring(0, cursorIndex);
subStr = subStr.replace(/\s*/g, '');
return subStr.length - 1;
}
/**
* 根据格式化之后的值和期望光标位置的前一个字符下标,计算出光标的位置
*
* @param {string} value - 格式化之后的值
* @param {number} charIndex - 光标前一个字符的下标
* @return {number} 光标的位置
*/
function getCursorIndex(value, charIndex) {
if (charIndex < 0) {
// 如果期望光标前面没有字符,则光标位置为 0
return 0;
}
// 计算格式化之后,字符所在的位置
let curCharIndex;
let count = -1;
for (let i = 0; i < value.length; i++) {
if (value.charAt(i) !== ' ') {
count += 1;
}
if (count === charIndex) {
curCharIndex = i;
break;
}
}/**
* 格式化手机号,133 3333 3333格式
*
* @param {string} value - 输入框内容
* @return {string}
*/
function format(obj){
let value = valid(obj.value);
if (!value) {
return value;
}
value = value.replace(/\s*/g, '');
if (value.length > 3 && value.length < 8) {
return value.substr(0, 3) + " " + value.substr(3);
} else if (value.length >= 8) {
return value.substr(0, 3) + " " + value.substr(3, 4) + " " + value.substr(7);
}
console.log(value);
obj.value = arr.join(' ');
return obj.value;
}
}