聊聊富文本编辑
前端富文本编辑的场景还是挺多的,所以开发中就需要应用到各种富文本插件,比如百度的UEditor,kindeditor,simditor,wangEditor 等等,总之线上有很多插件。
虽然插件多使用起来也很方便,但是无论如何都不能很好的满足项目的需求,因为很多时候,我们需要得到的内容是一个干净的富文本,比如简书的富文本编辑输出的就是非常简单富文本内容。
对于得到干净的富文本内容,我找了很多线上的开源富文本编辑器插件也没有发现一个靠谱的。因为运营最常用的编辑方式就是复制粘贴,这就导致了富文本里包含了不可的标签和样式,导致污染了富文本编辑器。
其实我想要的编辑器也很简单,支持图文混排,文字和图片外层都包含一个P标签,格式如下:
<p>第一段文本</p>
<p>第二段文本</p>
<p>![](http:https://img.haomeiwen.com/i80274/0b09df1b96e9fff0.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</p>
<p>第三段文本</p>
如此简单的要求,所以我想自己去实现一个这样的富文本。然而,不得不说富文本真的是一个很大坑,线上资料很少,案例也很少,只能硬着头皮找文档,然后不断尝试。
然后过程中遇到问题会有下面这些:
1、首先得到一个contenteditable属性,这个属性允许普通的标签可编辑,这个基础非常实用。
2、我们在可编辑的div里输入内容,会得到如下格式的内容
<div contenteditable>
123123
<div>123123123</div>
<div>123123123</div>
</div>
这里就有两个问题了,一起始位置没有P标签包裹,二段落是通过div包裹的。
这里我们可以在起始位置初始一个P标签就能解决了。
<div contenteditable>
<p><br></p>
</div>
这也黑科技,原理我也不太懂,不过要注意的是,在删除编辑器内容的时候,会把初始的P标签删除掉,再次输入就还是会有问题,所以我们需要监听删除,然后做相应的处理。
3、复制网页内容粘贴到富文本中。
这里如果不做处理的话就会出现污染富文本的问题,所以我们也需要监听粘贴事件,只取剪切板的文本内容。
//粘贴监听
editorBody.addEventListener("paste",function(e){
var text = null;
text = (e.originalEvent || e).clipboardData.getData('text/plain');
document.execCommand("insertText",false,text);
e.preventDefault();
},false);
4、插入图片问题。
虽然execCommand这个函数给我们提供插入图片的方法insertImage。
insertImage
在插入点插入一张图片(删除选中的部分)。需要一个image SRC URI作为参数。这个URI至少包含一个字符。空白字符也可以(IE会创建一个链接其值为null)
但是,我们的需求是用P包裹img标签,所以我们可以用另外一函数insertHTML
//插入图片
img.addEventListener("click",function(e){
let imgUrl = "http://video.yingtu.co/8/image/288559ae-3365-4130-b9dc-b7a5ef187dea.jpg";
document.execCommand("insertHtml", false, "<p>![](+imgUrl+)");
},false);
这样看起来基本是比较合理了,但是还是有一些细节的问题。比如光标停留在图片前后我们输入文字的话就会出现在img前后,而不会单独用P标签包裹。
不正常的格式:
<div contenteditable>
<p>1234![](1.jpg)123123</p>
</div>
正常的样式:
<div contenteditable>
<p>1234</p>
<p>![](1.jpg)</p>
<p>123123</p>
</div>
这个细节处理起来还很麻烦的,我是采用非常笨的方法去判断的
editorBody.addEventListener("keydown",function(e){
var rang = document.getSelection().getRangeAt(0);
if(e.code == "ArrowUp"||e.code == "ArrowDown"||e.code == "ArrowLeft"||e.code == "ArrowRight"||e.code == "Enter"){
}else if(e.code == "Backspace"){
if(e.target.children.length==1 && e.target.children[0].innerHTML =="<br>"){
e.returnValue=false;
}
//处理图片删除
if(rang.commonAncestorContainer.nodeName == "#text"
&&rang.endOffset == 0
&&rang.commonAncestorContainer.parentNode.previousSibling
&&rang.commonAncestorContainer.parentNode.previousSibling.children.length
&&rang.commonAncestorContainer.parentNode.previousSibling.children[0].nodeName=="IMG"){
rang.commonAncestorContainer.parentNode.previousSibling.remove();
e.returnValue=false;
}
}else{
//处理前后添加文字
if(rang.commonAncestorContainer.nodeName == "P"
&&rang.commonAncestorContainer.children.length==1
&&rang.commonAncestorContainer.children[0].nodeName =="IMG"){
document.execCommand("insertHtml", false, "<p><br></p>");
}
}
},false);
就会在键盘按下时判断是否是图片的位置,然后做相应的处理,删除的时候也需要做相应的处理。
最后提供一个getValue和setValue的方法,用于获取和编辑文本内容,在页面中使用:
<div id="editor"></div>
<button id="submit">获取</button>
<script type="text/javascript" src="editor.js"></script>
<script type="text/javascript">
var zhuEditor = new ZhuEditor("#editor");
zhuEditor.setValue("<p>12123123</p>");
var submit = document.getElementById("submit");
submit.onclick = function(){
console.log(zhuEditor.getValue());
}
</script>
知乎上说富文本是一个坑,这个说法是没错的,不过,自己简单的用100行代码实现简单的需求还是可以接受的,给大家提供以上的一些参考。如果你也遇到相同的问题,希望可以对你有一点帮助。
预览地址:https://zhuss.github.io/editor/index.html