前言
最近开发基于Web版的富文本,要实现PC与移动端互通,而且还有一些基础的操作,故而参考了许多开源框架和富文本JS框架。
实现富文本技术主要分为三类:ceretext、textkit、webview。这三类各有优劣,开发的难度、方式、用户体验也是有非常大的不同,这里不详细说明,具体的可以找度娘。
这里我主要使用的基于webview富文本框架:ZSSRichTextEditor,再此基础上进行的改造和开发。
技术实现
在ZSSRichTextEditor框架中,有很多的文件,猛地一看有些不知所措。仔细阅读后才发现,主要的文件只有4个,其他的基本都是UI及一些交互。
- ZSSRichTextEditor.js
- zsseditor.html
- JSBeautifier.js(其中style_html方法还没有用)
- jQuery.js
在 ZSSRichTextEditor.js 文件中,使用的技术就是Web API接口中Document的方法。
当一个HTML文档切换到设计模式 designMode时,文档对象暴露 execCommand 方法,该方法允许运行命令来操纵可编辑区域的内容。大多数命令影响文档的选择(粗体,斜体等),而其他命令插入新元素(添加链接)或影响整行(缩进)。当使用contentEditable时,调用 execCommand() 将影响当前活动的可编辑元素。
技术实现并不复杂,对于一个不懂得js的移动开发人员来说,看几天也能明白个大概,也可以上手调试及修改。
我的建议是不要完全照搬,还是根据自己的需求,进行适当的改动。
开发中遇到的坑
1、使用UIWebView 还是 WKWebview?
众所周知,wkwebview的性能要比前者高很多,我在开发的时候,也是首选它,但是在个别的js方法中,会遇到兼容性的问题。
例如:插入的视频不能显示,插入的图片不能使用file:///来显示,只能通过转base64才可以,及其他。
这些问题会给你带来很多的困扰,不止是显示,还有后续的缓存操作,导出HTML,删除操作等。
为了更简单的开发,我选择了UIWebView,虽然牺牲一些性能,但是操作体验也是ok的,没有明显的差距。
2、webview删除工具条
通过分类的方法可以删除工具条,UIWebView和WKWebView都可以,具体可以参考文章:
ios - 删除WebView键盘上的工具条
3、中文输入法样式不改变的问题
字体加粗:document.execCommand('bold', false, null)
具体的问题是这样的:对字体“加粗”后,再次调用“加粗”,使用英文和数字是没有任何问题的,字体变成不加粗的状态;使用中文输入法时,没有确认之前,字体都是正常的,当选中确认的文字后,字体就会跟前面的样式走,还是加粗。
这个问题不止是 加粗,斜体,下划线也是如此,可能是webview的一个bug,要解决这个问题,就需要在输入的前方增加一个'‌'字符。
'‌':放在电子文本的两个字符之间,抑制本来会发生的连字,也就是不使用之前的样式。
具体的用法如下:
if (document.queryCommandState('bold')) {
document.execCommand('bold', false, null);
document.execCommand('insertHTML', false, '‌');
} else {
document.execCommand('bold', false, null);
}
以上的方式虽然可以解决富文本中兼容性的bug,但是逻辑写起来特别麻烦,而且还存在其他的漏洞,今天无意间又发现了一种新的解决方案。
if (document.queryCommandState('bold')) {
document.execCommand('bold', false, 'div');
} else {
document.execCommand('bold', false, null);
}
经过了一段时间的测试和开发,以上两种方式必须同时使用,才能解决webview中设置字体的bug
4、换行光标跟随问题
在 ZSSRichTextEditor 文件中,有一个方法:calculateEditorHeightWithCaretPosition 是通过计算文字的高度与当前偏移量做计算,然后进行滚动,达到跟随输入光标位置的现象,getCaretYPosition方法中,
我增加一个normalize() 方法,解决了一些兼容性的问题。
normalize() 方法移除空的文本节点,并连接相邻的文本节点。
zss_editor.getCaretYPosition = function() {
var sel = window.getSelection();
// Next line is comented to prevent deselecting selection. It looks like work but if there are any issues will appear then uconmment it as well as code above.
//sel.collapseToStart();
var range = sel.getRangeAt(0);
var span = document.createElement('span');// something happening here preventing selection of elements
range.collapse(false);
range.insertNode(span);
var topPosition = span.offsetTop;
var spanParent = span.parentNode;
spanParent.removeChild(span);
spanParent.normalize();
return topPosition;
}
5、输入中文时,光标跳动问题
在最后一行,换行到新的一行进行输入的时候,如果是汉字输入,会产生联想输入条,在还没有确定输入内容的时候,UIWebView是不知道你需要的高度的,这个时候,由于触发了selectionchange,会导致输入时候,整个界面不断的抖动,因此在webview的最后面,强制插入一个空白的div(footer),使得输入始终是在已有的区域范围内的,然后在键盘弹起和收回的时候,设置编辑内容和footer的高度。
<!-- ZSSRichTextEditor Editable Content -->
<div id="zss_editor_content" class="zs_editor_content" contenteditable="true" placeholder="">
<!-- insertHTML -->
</div>
<!-- Footer -->
<div id="zss_editor_footer"></div>
6、键盘自动唤起
在进入编辑器时,调用js方法设置焦点,达到唤起键盘的目的,但是无论怎么调用,都是不管用。后来才发现UIWebView的一个属性,设置以后就ok了,但是弹起的效果不太好看,不知道为什么。
self.keyboardDisplayRequiresUserAction = NO;
Available in iOS 6.0 and later.默认是YES
如果设置为YES,用户必须明确的点击页面上的元素或者相关联的输入页面来显示键盘;如果设置为NO,一个元素的焦点事件导致输入视图的显示和自动关联这个元素。
演示图:
222.gif参考文章:
document.execCommand API文档
HTML6种空格的区别  ;&ensp;&emsp;&thinsp;&zwnj;&zwj;
利用contenteditable属性与execCommand()方法制作简易富文本编辑器
iOS的webview下的一个bug
基于 UIWebView 的富文本编辑器实践
网友评论
1、”<strike>很高兴</strike>认识你“,当用户删除或者移动光标导致“兴”之后,理论上应该输入的文字应该属于strike标签内的,但是应该有一个空格,所以就还是之前的样式。
我感觉最根本的原因还是插入了一个空格导致的
2、删除到例子1中的“兴”之后需要多点击移除删除按钮去删除看不见的空格
// 单纯的删除操作,可以检测到最后一个是插入的空格来手动删除它。但是移动光标这种就很坑了啊。目前想到的是做一个和文本框一样大小的隐藏 div 然后监听 div 的 click 事件,然后去获取当前光标的位置,去做类似于删除操作的逻辑。
而且没有看懂博主的方案2啊,设置了那个'div'没有效果呢?
楼主有没有遇到提到的这两个bug啊,怎么处理的。
求分享
1、当第一次进去编辑页面的时候,第一个textView的默认高度是否是屏幕的高度,否则点击空白进入不了编辑状态,当然加手势可以,但是判断逻辑会增加。
2、当在一段文字中间插入图片,是否把之前的文字拆分,如果拆分,当我把图片删除,怎么保证之前的图文还在同一个cell里面,是通过状态判断么?
2、添加一个自定义view(custool)
3、根据键盘的高度,来设置custool的高度,并且改变webview的高度
InsertOrderedList 切换当前选中区是编号列表还是常规格式化块。
InsertUnorderedList 切换当前选中区是项目符号列表还是常规格式化块。
还需要的是列表前面是方形,其他两种是圆点和数字的