`
yiminghe
  • 浏览: 1433122 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

ckeditor 核心函数图解

阅读更多

序言:

 

    承接上文,ckeditor 既然不用 execCommand ,那么编辑器的格式化功能就只能通过自己手动添加格式化标签实现,一般格式化包括四步:

1.得到选择位置,

2.抽取选择节点集,

3.对节点集格式化,

4.格式化节点集加入文档,

    其中第1步在标准浏览器中可以通过w3c range 直接得到,而ie 则要费些周折,而2,3,4则是真正麻烦的地方,2要保证节点集的完整性,3要防止格式化代码的冗余,4则要判断最终插入的位置。其中第2步其实w3c range已经实现,但是ck为了最大程度地兼容各个浏览器,不依赖于浏览器,使用了 dom2-core 来自己实现对应功能,毕竟参照规范:The Range interface provides methods for accessing and manipulating the document tree at a higher level than similar methods in the Node interface. The expectation is that each of the methods provided by the Range interface for the insertion, deletion and copying of content can be directly mapped to a series of Node editing operations enabled by DOM Core. In this sense, the Range operations can be viewed as convenience methods that also enable the implementation to optimize common editing patterns.(在这一点上又同css选择器引擎构建 类似)

 

      本质上文档是DOM树,抽取插入格式化节点集都是对树进行操作,树(tree data structure) 作为计算机科学中一种重要的数据结构,在web信息抽取编译语法树 )以及自然语言处理领域 已经达到了广泛的应用,而在浏览器前端由于复杂度多由服务器承担而不受重视(近来也开始渐受关注,参见 high performance javascript ),基于浏览器的编辑器随着用户的持续修改,会对dom树产生巨大的变动,相应地树操作算法对于编辑的流畅性变得至关重要。

 

Demo :


ckeditor core

 

 

核心函数:

 

range.extractContents


对应于规范的同名函数 extractContents ,将用户选中的区域节点集完整抽取出来作为 DocumentFragment 返回:(注意连续文本节点需要 splitText 操作),并且将选择范围 collpase 到一点。


抽取前:


绿色表示文本节点,红色表示标签

 

 

 

 

抽取后得到的文档碎片:


包含了完整的选择区域节点集,需要注意 为了完整性: 起始节点所在树路径的节点必须复制下来:

 

 

 

 

抽取后的文档树:

 

注意从开始节点到结束节点,以DFS(深度优先)遍历遇到的节点如果不在起始节点的树路径上则直接删除:

 

 

dom.mergeSilblings :

 

随着用户的添加删除,必然剩下很多没有文字节点的空标签,如<span style="xx">1</span><span></span>...,ckeditor在下次添加格式标签后会进行标签合并工作:


合并前:

 


合并后:

 

 

 

 

style.applyInlineStyle

 

和规范中的 surroundContents 类似,简单的说就是调用了 extractContents 和 mergeSilblings ,生成对应的格式标签包裹抽取节点集,然后插入到原来的选择位置。不过这个函数也有一些处理:

 

1.range 切分

 

将 range 切分为一系列更小范围的 range,使得格式标签根据 xhtml dtd 嵌套规则得以能够包裹这个 range 的 html 内容,譬如选择了 p 标签以及其内的所有内容调整字体大小,但是格式标签<span style="font-size:xx"></span>并能直接包裹 p ,而只能包裹 p 内的行内元素,然后再由 p 来包裹 格式标签,遇到选择多个 p 时更需要逐一处理,即切与分。 


2.抽取节点集的格式清理 ,子节点重复的style属性删除


包裹后格式清理前:

 

<span style="xx">

<span style="xx">
zz
</span>

yy

</span>

 

包裹后格式清理后:

 

<span style="xx">

<span>
zz
</span>

yy

</span>

 

3.无属性,无样式的标签清理


清理前:

 

<span style="xx">

<span>
zz
</span>

yy

</span>
 

清理后:

 

<span style="xx">

zz

yy

</span>

 


总结:


上述只是大概说明了实现编辑操作的必要需求,但是性能则取决于具体算法实现,或许前端是时候看看图论 了。


 

PS:调试相关

 

建议 firefox(代表标准浏览器下)使用 firebug 调试,ie使用 ie8 自带调试工具,双屏对比调试就能对浏览器差异有更深刻的认识了:

 

 

 

(修订于:2010-08-08)

 

 

  • 大小: 49.7 KB
  • 大小: 39.3 KB
  • 大小: 37.4 KB
  • 大小: 42.5 KB
  • 大小: 37.6 KB
  • 大小: 316.2 KB
分享到:
评论
10 楼 yiminghe 2011-02-18  
qzlake 写道
这些图片用PowerPoint画的吧?


截图工具 snagit
9 楼 qzlake 2011-02-18  


这些图片用PowerPoint画的吧?
8 楼 yiminghe 2010-07-28  
campaign 写道
比如在ie下,选区是跨td的,我要对里边的文字加粗strong,那不能简单的用extractContents,来做,因为根据dtd,td肯定不能放在strong里,那么就要对这个大的 range进行小的细分,小到每个小的range包含的内容都能放到strong,那么在用extractContent取出来,style.js就是干这个事情的
ff可以自动吧range分了,但是得到的range要shrink,才好在操作


多谢,我没看仔细,理解不全面!确实styles/plugin.js applyInlineStyle 里面有循环,内层循环根据 dtd 从原先的 range 中切出 小range(styleRange)再进行extractContents格式化
7 楼 campaign 2010-07-28  
比如在ie下,选区是跨td的,我要对里边的文字加粗strong,那不能简单的用extractContents,来做,因为根据dtd,td肯定不能放在strong里,那么就要对这个大的 range进行小的细分,小到每个小的range包含的内容都能放到strong,那么在用extractContent取出来,style.js就是干这个事情的
ff可以自动吧range分了,但是得到的range要shrink,才好在操作
6 楼 yiminghe 2010-07-27  
campaign 写道
将range按着操作切分成小的range,以满足操作的要求,我觉得这个是ck的很核心的东西,等于是做了原生命令的事情,对于后续的对于小的range进行extractContents,在按着自己的要求添加相应的元素,使其各个浏览器保持风格统一。我认为walker是为range服务的,但range是为style服务的

range切小range?ie下就一个range转换成标准兼容返回 [ range ],其他浏览器稍微转一下返回ranges,然后对每个range操作既可以了,这个不算很核心吧,核心还是树操作
遍历是很重要的组成部分,包括(逐渐抽象):
1.next/preSourceNode
2.walker
3.domiterator
5 楼 campaign 2010-07-27  
将range按着操作切分成小的range,以满足操作的要求,我觉得这个是ck的很核心的东西,等于是做了原生命令的事情,对于后续的对于小的range进行extractContents,在按着自己的要求添加相应的元素,使其各个浏览器保持风格统一。我认为walker是为range服务的,但range是为style服务的
4 楼 yiminghe 2010-07-26  
campaign 写道
既然你说到了style,我认为你应该先说style,在说extractContent,等
因为dom树上的选中的range很多都不能直接用extractContent,来切出来,尤其是ie,没有多range的概念
要先用style,切分割range成小块,再在小块的range中用extractContent,


呵呵,这个不难,就是style里循环一下,我觉得没有必要说吧
3 楼 campaign 2010-07-26  
既然你说到了style,我认为你应该先说style,在说extractContent,等
因为dom树上的选中的range很多都不能直接用extractContent,来切出来,尤其是ie,没有多range的概念
要先用style,切分割range成小块,再在小块的range中用extractContent,
2 楼 yiminghe 2010-07-22  
luolonghao 写道
:idea:,分析的不错,我觉得tinymce的架构也不错,也可以一起研究一下。

好啊,不过更希望你能听取yubo建议过来这边一起研究
1 楼 luolonghao 2010-07-22  
:idea:,分析的不错,我觉得tinymce的架构也不错,也可以一起研究一下。

相关推荐

Global site tag (gtag.js) - Google Analytics