Div+contenteditable 模拟 textarea 及存在的问题

在网页端实现聊天功能时,大多会采用textarea来实现,但是当需要在textarea中显示表情/图片时,就不好实现了。这时候普遍的方法是给div添加contenteditable属性,来模拟textarea,而在div中显示图片及表情就可以轻松实现了。

The contenteditable global attribute is an enumerated attribute indicating if the element should be editable by the user. If so, the browser modifies its widget to allow editing. The attribute must take one of the following values:

  • true or the empty string, which indicates that the element must be editable;
  • false, which indicates that the element must not be editable.
    If this attribute is not set, its default value is inherited from its parent element.

focus定位问题

使用div模拟textarea后会发现一个问题,就是每次点击输入框或者使用$('#tag').focus()让输入框聚焦时,光标都会定位在最开始的位置。
解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function placeCaretAtEnd(el) {
el.focus();
if (typeof window.getSelection != "undefined"
&& typeof document.createRange != "undefined") {
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(false);
textRange.select();
}
}

placeCaretAtEnd( document.getElementById("content") );

粘贴复制问题

当使用div作为输入框时,你会发现,粘贴复制到的全是HTML结构,而我们想要的是粘贴复制纯文本,就像textarea那样。解决方法就是监听div的粘贴事件,然后在粘贴事件中匹配替换HTML元素,返回纯文本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var $plainText = $("#plainText");

$plainText.on('paste', function (e) {
window.setTimeout(function () {
$plainText.html(removeAllTags(replaceStyleAttr($plainText.html())));
}, 0);
});

function replaceStyleAttr (str) {
return str.replace(/(<[\w\W]*?)(style)([\w\W]*?>)/g, function (a, b, c, d) {
return b + 'style_replace' + d;
});
}

function removeTagsExcludeA (str) {
return str.replace(/<\/?((?!a)(\w+))\s*[\w\W]*?>/g, '');
}

function removeAllTags (str) {
return str.replace(/<\/?(\w+)\s*[\w\W]*?>/g, '');
}