请教前端达人,知乎的编辑器是怎样实现这个功能的?

2016-02-19 13:29:05 +08:00
 wangleineo

在线富文本编辑器里面,做以下操作:

  1. 选中一串文字,点击“ B ”把它加粗。
  2. 把游标移到这串文字的中间,再点“ B ”。
  3. 输入一些文字。

在大部分其他的编辑器里面,到第二步的时候,刚才加粗的所有文字都回复了正常状态:

而知乎的编辑器的效果是这样:

从 HTML 上面看,它是把一个<b></b>节点拆成了三个节点:
<b>some</b>inserted text<b>bold text</b>.

问题是:在 contentEditable 上的编辑操作只能更新当前节点的文本内容,也就是说如果没有 js 干预,效果是这样:
<b>some inserted text bold text</b>

知乎的编辑器是怎样完成分拆动作的?
监听 keypress 事件?那输入中文怎样实现?
监听 change ?

7846 次点击
所在节点    JavaScript
15 条回复
morethansean
2016-02-19 13:45:47 +08:00
知呼这个不是浏览器的默认处理方式吗……
wangleineo
2016-02-19 13:52:11 +08:00
@morethansean 不是,浏览器本身并不知道已经取消了 Bold 样式。
EXDestroyer
2016-02-19 13:55:45 +08:00
表示 ueditor 也跟知乎一样
kfll
2016-02-19 13:59:05 +08:00
wangleineo
2016-02-19 13:59:18 +08:00
刚刚看了一下 UEditor ,行为和知乎的一样,用了一种很取巧的方法:在步骤 2 的时候,它就把原来的<b></b>分拆成两个,并在中间增加了一个不可见字符节点,<b></b>& 1234 ;<b></b>,这样,接下来插入文字就是在不可见字符的节点上插入的,是没有加粗的。

但是知乎的编辑器不是这样做的。
morethansean
2016-02-19 14:31:49 +08:00
@wangleineo 我用 Chrome 试了,浏览器本身的行为就是这样。你明明第二次点了 B ,浏览器怎么可能不知道呢?
你随便找个 contenteditable 的输入框,打一串文字按 command/ctrl + b , 然后点一个中间的地方,再按下 command/ctrl + b ,然后打字,不就是你所谓的效果吗?明明按了第二次 B 取消了 bold ,为什么叫浏览器不知道呢?还是我理解有问题?

你要是觉得自己按 bold 和 js 执行的不一样(其实就是一样的),那我给你写了个小 demo : https://jsfiddle.net/cvqzfbb2/
wangleineo
2016-02-19 14:39:16 +08:00
@morethansean
理解问题,我说的 B 按钮是编辑器自己实现的控件。

ctrl-B 实现的浏览器的 bold 状态切换, 有 javascript 的接口吗?
morethansean
2016-02-19 14:41:51 +08:00
@wangleineo ……有啊,你看我的 demo

都有哪些命令的话: https://w3c.github.io/editing/execCommand.html

editing 相关: https://w3c.github.io/editing/
wangleineo
2016-02-19 15:00:36 +08:00
@morethansean 谢谢。
zhihu 就是用 execCommand 实现的
jsq2627
2016-02-19 15:09:21 +08:00
其实非常简单的

判断当前光标下是否为粗体
document.queryCommandState('bold')

切换当前光标下粗体状态
document.execCommand('bold')
wangleineo
2016-02-19 15:27:02 +08:00
@morethansean
那这样的话,写一个富文本编辑器不是很简单吗?只要用 execCommand 做命令映射就好了,所有的 DOM 变更操作都由浏览器实现好了。

尝试做这个主要是因为看到这个问题:
https://www.zhihu.com/question/26739121

那些说很难做的,到底难度在哪里?
morethansean
2016-02-19 16:50:01 +08:00
@wangleineo 看你的富文本编辑器有哪些功能了。如果就是那几个很基本的功能,调调 api 就能实现的那是可能比较简单。

即便如此实际上你做的时候可能遇到各种各样奇怪的 case ,比如从外部复制粘贴过来的时候。还有就是浏览器兼容性问题。

不过不管简单还是难,对最终数据的处理比如过滤什么的也是比较麻烦的。安全性也是很重要的呢。
wangleineo
2016-02-19 17:13:02 +08:00
@morethansean 同意。

也有一些编辑器不是完全利用 execCommand 来实现编辑操作的,比如 Medium 的编辑器实现了一个自定义的 Model ,操作是对这个 Model 的修改,然后再把 Model 映射成 DOM 。

https://medium.com/medium-eng/why-contenteditable-is-terrible-122d8a40e480#.8ew1h1af2

W3c 的文档里提到了这篇博客,业界实践也能推动标准。
jsq2627
2016-02-19 17:35:17 +08:00
@wangleineo 浏览器自带的那一套主要问题是不同浏览器有不少小差异,比如剪切板 API 差异,换行处理的差异,键盘事件差异, executeCommand 行为不一致等等,要完全搞清楚这一套还是得看不少源码好好总结的。

DOM 模型针对一个编辑器而言过于复杂了,另外 executeCommand 在不同浏览器行为略有差异,所以有些编辑器搞一个自己的文档模型,这样也好保证各种命令行为一致。

编辑器还有一个难题,是怎么抽象一个可扩展的接口,方便扩充能力。 ckeditor 扩展做得好,生态才比较好,想要什么功能,网上都有插件。

另外还了解过 quill.js ,开发很活跃,代码很清晰。也是自己搞的一套文档模型,很有学习价值。不过这家伙原来用 coffeescript ,一次推翻重来改用 ES6 重写,现在已经不敢在产品里用它了。。

另外推荐 tower.im 出的 simditor ,这个是没有自己的文档模型的,但是已经很成熟了,也在不少产品中有使用( teambition 也是用的它,哈哈)。我们产品上个月刚换它,效果还不错的,也很方便自己扩展。
minggeJS
2016-02-24 19:50:56 +08:00
我以前写过这种编辑器,就是比较费时间,但是这东西真心不难

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/257613

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX