因为项目需求,前台做一个聊天编辑器。
由于需要插入表情,而非纯文本,textarea显然是不能支持。这种需求的。这种需求就我所知只能用div 来实现。
一个div元素,要让其可编辑,也就是可读写,contenteditable
属性是最常用方法,做前端的基本上都知道,原理就是在这个div 添加html。模拟展示。输入框。
项目需求大概是这样的
遇到的坑:
然而,这个属性,除去兼容性不说,还是存在一些比较坑爹的小bug的。搜了不少文章,比如张鑫旭大神的纯文本方式输出div内容,链接狂戳以下,帮助还是很大的:
http://www.zhangxinxu.com/wordpress/2016/01/contenteditable-plaintext-only/
但是在真正使用的时候,发现了一个很大很大的坑。一般的编辑器,都需要事实字数统计功能。在测试纯文本contenteditable时发现了如下几个坑人的问题:
1、 换行时,字数+2,再次输入一个字符,字数不变,然后再逐渐+1。(字数统计方式当然是获取html然后计算length)。
有的浏览器换行会加P元素,有的会加<div><br><div>,我遇到的就是这个,还是先加两个,然后才逐渐加1个。和上述一样。(ps:这种问题用shift+enter 是可以避免 直接加<br>的,但是显然这个不能解决根本问题,因为人的习惯就是enter换行,一但按enter后,你输入的文字,将在<div><br><div>下。个人感觉后续很难处理。)
2、 在末尾换行的时候,会加出一个看不见br。按回车br出现但页面没换行,在按一次会在加一个br,行是换了。但是末尾这 个换行是存在2个br的。那个末尾多出的br.(死活干不掉,选了也选不着。注:别在末尾换行,可以避免这个坑。当然 了,用户是不可能不踩这个坑的,随便打个字,在那个字前面换行。编辑结束在删掉即可。)
3、无法再光标处插入新加文字或者表情img标签。
4、 删除字符后,在dom里会留下一堆双引号。(这个我不打算理会,因为输出的字符串是没有这些家伙的。)
网上查看了一下,没什么可用信息。于是自己想了一套方案处理这些坑。
方案:
主要方法。解决第三个坑的主要问题。在光标处插入表情的img标签
/*光标处插入html代码,参数是String类型的html代码,例子:"<p>猪头诺</p>"*/
function insertHtml(html) {
var sel, range;
if (window.getSelection) {
// IE9 或 非IE浏览器
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
// Range.createContextualFragment() would be useful here but is
// non-standard and not supported in all browsers (IE9, for one)
var el = document.createElement("div");
el.innerHTML = html;
var frag = document.createDocumentFragment(),
node, lastNode;
while ((node = el.firstChild)) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
// Preserve the selection
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}
} else if (document.selection && document.selection.type != "Control") {
// IE < 9
document.selection.createRange().pasteHTML(html);
}
}
先介绍这个方法,是解决坑1也需要这个方法。
现在我将用他来解决坑1.
我的解决思路是,阻止回车事件。然后由自己写的逻辑来走。就是我让它回车加<br>就可以了。将它写在阻止事件中。
阻止事件。代码如下
/*阻止回车事件*/
$(window).keydown( function(e) {
var key = window.event?e.keyCode:e.which;
/*获取用户按键,如果是回车,则不执行任何*/
if(key.toString() == "13"){
/*调用光标插入方法,在光标处插入 换行*/
insertHtml("<br>");
return false;
}
});
如此一来就把坑1 给填了。回车之后只有<br>,而不会出现其他的标签了。
现在我们来解决坑2.末尾换行会多一个br。死活删不掉的一个<br>。不看代码是看不到的。但是是存在的。
我的解决思路是。这个家伙占据这个div输出框4个字节。我可以监听这个div内容。当它的length为4的时候,判断其html。如果为<br>则清空这个<div>。这个是解决删除全部文本。删不掉。它是在末尾,提交的时候可以获取br个数。干掉最后一个即可。不过末尾有个br,其实影响,个人感觉不大。所以我苦恼的是删除时候它占据了在内容里面
监听方法如下:
/*
* 输入框监听事件
* */
$('.js_edit_area').bind('input propertychange', function() {
var len =$(this).html().length;
var x = 600-len;
/*限制只能输入600字*/
if(x<=600){
}
/*判断长度,干掉多余的br*/
if(len==4){
var br = $('.js_edit_area').html();
if(br=="<br>"){
$('.js_edit_area').html("");
$(".js_editor_tip em").html("600");
}
}
/*也可以获取br元素,干掉最后一个br即可,逻辑写在提交那里*/
});
以上是个人的一些解决方案。记录成长,也分享帮助需要的你们。