上次写了一篇关于js设置光标插入文字和HTML的文章,里面详细的写了几种关于获取光标位置和设置光标位置的代码,今天来分享以下ie10之下的浏览器不记录光标位置的解决思路。
需求分析
比如你需要在一个文本框或者可编辑框的第10个字符位置插入一个表情,这时你点击某个位置,弹出一个表情列表选择框,然后选中你需要的表情,执行的文本框focus(),把代表表情的字符插入到我们上一次聚焦的位置,高级浏览器妥妥的还是原来那个位置,可IE却是聚焦到了文本框的最前面去了。
解决方法
我去看了一下微博的那个输入框,发现它把当前的光标位置记录在了文本框里面,使用一个range=’8’这样子的属性来记录,这样子即使我们在别的地方怎么点,下一次回到这个文本框时还能知道上次光标的位置。有了这个光标位置,在插入文本前把光标放到这个位置妥妥的。
代码实现
根据之前的文章,我们很容易的提取出几个代码:
1.插入文本
function insertAtCursor(myField, myValue) {
//IE support
if (document.selection) {
console.log('ie');
myField.focus();
sel = document.selection.createRange();
sel.text = myValue;
}
//MOZILLA and others
else if (myField.selectionStart || myField.selectionStart == '0') {
console.log('modern');
var startPos = myField.selectionStart;
var endPos = myField.selectionEnd;
myField.value = myField.value.substring(0, startPos)
+ myValue
+ myField.value.substring(endPos, myField.value.length);
myField.selectionStart = startPos + myValue.length;
myField.selectionEnd = startPos + myValue.length;
} else {
myField.value += myValue;
}
}
2.获取光标位置
function getCursorPos(input) {
if ("selectionStart" in input && document.activeElement == input) {
return {
start: input.selectionStart,
end: input.selectionEnd
};
}
else if (input.createTextRange) {
var sel = document.selection.createRange();
if (sel.parentElement() === input) {
var rng = input.createTextRange();
rng.moveToBookmark(sel.getBookmark());
for (var len = 0; rng.compareEndPoints("EndToStart", rng) > 0; rng.moveEnd("character", -1)) {
len++;
}
rng.setEndPoint("StartToStart", input.createTextRange());
for (var pos = { start: 0, end: len }; rng.compareEndPoints("EndToStart", rng) > 0; rng.moveEnd("character", -1)) {
pos.start++;
pos.end++;
}
return pos;
}
}
return -1;
}
发现上面这段代码在ie8兼容模式中获取到的值有问题。找到另外一段代码:
//获取选择域位置,如果未选择便是光标位置
function getSelection(el) {
return (
('selectionStart' in el && function () {
var l = el.selectionEnd - el.selectionStart;
return {
start: el.selectionStart,
end: el.selectionEnd,
length: l,
text: el.value.substr(el.selectionStart, l)
};
}) ||
(document.selection && function () {
el.focus();
var r = document.selection.createRange();
if (r === null) {
return {
start: 0,
end: el.value.length,
length: 0
}
}
var re = el.createTextRange();
var rc = re.duplicate();
re.moveToBookmark(r.getBookmark());
rc.setEndPoint('EndToStart', re);
return {
start: rc.text.length,
end: rc.text.length + r.text.length,
length: r.text.length,
text: r.text
};
}) ||
function () {
return null;
}
)();
}
和这个差不多类似:
function getInputSelection(el) {
var start = 0, end = 0, normalizedValue, range,
textInputRange, len, endRange;
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
start = el.selectionStart;
end = el.selectionEnd;
} else {
range = document.selection.createRange();
if (range && range.parentElement() == el) {
len = el.value.length;
normalizedValue = el.value.replace(/\r\n/g, "\n");
// Create a working TextRange that lives only in the input
textInputRange = el.createTextRange();
textInputRange.moveToBookmark(range.getBookmark());
// Check if the start and end of the selection are at the very end
// of the input, since moveStart/moveEnd doesn't return what we want
// in those cases
endRange = el.createTextRange();
endRange.collapse(false);
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
start = end = len;
} else {
start = -textInputRange.moveStart("character", -len);
start += normalizedValue.slice(0, start).split("\n").length - 1;
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
end = len;
} else {
end = -textInputRange.moveEnd("character", -len);
end += normalizedValue.slice(0, end).split("\n").length - 1;
}
}
}
}
return {
start: start,
end: end
};
}
3.设置光标位置
function setCursorPos(input, start, end) {
if (arguments.length < 3) end = start;
if ("selectionStart" in input) {
setTimeout(function() {
input.selectionStart = start;
input.selectionEnd = end;
}, 1);
}
else if (input.createTextRange) {
var rng = input.createTextRange();
rng.moveStart("character", start);
rng.collapse();
rng.moveEnd("character", end - start);
rng.select();
}
}
4.记录更新光标位置
function updateRange(input){
var pos = getCursorPos(input);
input.setAttribute('range',pos.start);
}
$("#textarea").on("keyup click",function(e) {
updateRange(this);
})
5.点击插入文字
$("#button").click(function(){
var input = $('#textarea')[0];
var range = parseInt(input.getAttribute('range'))|| input.value.length || 0;
setCursorPos(input,range);
setTimeout(function(){
insertAtCursor(input,'test');
updateRange(input);
},1);
})
注意:上面的这个setTimeout定时器是必须的,如果不加,在插入文字时获取到的光标位置时错误的,需要等待上面的执行完成才能执行,我在IE9上面就遇到这个坑。
下一篇将会结合这篇文章的代码讲讲关于光标选中和复制、刻度之类相关的兼容性。
