JavaScript实现富文本编辑器

论坛 期权论坛     
选择匿名的用户   2021-5-30 00:10   182   0
<p>近期项目中需要开发一个兼容PC和移动端的富文本编辑器,其中包含了一些特殊的定制功能。考察了下现有的js富文本编辑器,桌面端的很多,移动端的几乎没有。桌面端以UEditor为代表。但是我们并不打算考虑兼容性,所以没有必要采用UEditor这么重的插件。为此决定自研一个富文本编辑器。本文,主要介绍如何实现富文本编辑器,和解决一些不同浏览器和设备之间的bug。</p>
<h1>准备阶段</h1>
<p>在现代浏览器中已经为我们准备好了许多API来让 html 支持富文本编辑功能,我们没有必要自己完成全部内容。</p>
<h2>contenteditable&#61;”true”</h2>
<p>首先我们需要让一个 <code>div</code> 成为可编辑状态,加入<code>contenteditable&#61;&#34;true&#34;</code> 属性即可。</p>
<p>&lt;div contenteditable&#61;&#34;true&#34; id&#61;&#34;rich-editor&#34;&gt;&lt;/div&gt;</p>
<p>在这样的 &lt;div&gt;中插入任何节点都将默认是可编辑状态的。如果想插入不可编辑的节点,我们就需要指定插入节点的属性为 <code>contenteditable&#61;&#34;false&#34;</code>。</p>
<h2>光标操作</h2>
<p>作为富文本编辑器,开发者需要有能力控制光标的各种状态信息,位置信息等。浏览器提供了 <code>selection</code> 对象和 <code>range</code> 对象来操作光标。</p>
<h3>selection 对象</h3>
<p>Selection对象表示用户选择的文本范围或插入符号的当前位置。它代表页面中的文本选区,可能横跨多个元素。文本选区由用户拖拽鼠标经过文字而产生。<br> 获得一个 selection 对象</p>
<p>let selection &#61; window.getSelection();</p>
<p>通常情况下我们不会直接操作 <code>selection</code> 对象,而是需要操作用 <code>seleciton</code> 对象所对应的用户选择的 <code>ranges</code> (区域),俗称”拖蓝“。获取方式如下:</p>
<p>let range &#61; selection.getRangeAt(0);</p>
<p>由于浏览器当前可能存在多个文本选取,所以 <code>getRangeAt</code> 函数接受一个索引值。在富文本编辑其中,我们不考虑多选取的可能性。</p>
<p>selection 对象还有两个重要的方法, <code>addRange</code> 和 <code>removeAllRanges</code>。分别用于向当前选取添加一个 range 对象和 删除所有 range 对象。之后你会看到他们的用途。</p>
<h3>range 对象</h3>
<p>通过 selection 对象获得的 range 对象才是我们操作光标的重点。Range表示包含节点和部分文本节点的文档片段。初见 range 对象你有可能会感到陌生又熟悉,在哪儿看见过呢?作为一个前端工程师,想必你一定拜读过《javascript 高级程序设计第三版》 这本书。在第12.4节,作者为我们介绍了 DOM2 级提供的 range 接口,用来更好的控制页面。反正我当时看的一脸????这个有啥用,也没有这种需求啊。这里我们就大量的用到这个对象。对于下面节点:</p>
<p>JavaScript<br> &lt;div contenteditable&#61;&#34;true&#34; id&#61;&#34;rich-editor&#34;&gt;</p>
<p> </p>
<pre class="blockcode"><code>&lt;p&gt;百度EUX团队&lt;/p&gt;

</code></pre>
<p>&lt;/div&gt;</p>
<p>光标位置如图所示:</p>
<p><img alt="" class="blockcode" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-87b0ce04a201d2ab2ffd910852551284"></p>
<p>image</p>
<p>打印出此时的 range 对象:</p>
<p><img alt="" class="blockcode" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-6fae989fc89727115229cb7d00ebfc2d"></p>
<p>image</p>
<p>其中属性含义如下:</p>
<ul><li>startContainer: range 范围的起始节点。</li><li>endContainer: range 范围的结束节点</li><li>startOffset: range 起点位置的偏移量。</li><li>endOffset: range 终点位置的偏移量。</li><li>commonAncestorContainer: 返回包含 startContainer 和 endContainer 的最深的节点。</li><li>collapsed: 返回一个用于判断 Range 起始位置和终止位置是否相同的布尔值。</li></ul>
<p>这里我们的 startContainer , endContainer, commonAncestorContainer都为 <code>#text</code> 文本节点 ‘百度EUX团队’。因为光标在‘度‘字后面,所以startOffset 和 endOffset 均为 2。且没有产生拖蓝,所以 collapsed 的值为 true。我们再看一个产生拖蓝的例子:</p>
<p>光标位置如图所示:</p>
<p><img alt="" class="blockcode" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-7d2d9f4a41aa119a22f60cdca9c1f90f"></p>
<p>image</p>
<p>打印出此时的 range 对象:</p>
<p><img alt="" class="blockcode" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-67a1454df8e94e86a45fa1983c5be653"></p>
<p>image</p>
<p>由于产生了拖蓝 startContainer 和 endContainer 不再一致,collapsed 的值变为了 false。startOffset 和 endOffset 正好代表了拖蓝的起终位置。更多的效果大家自己尝试吧。</p>
<p>操作一个 range 节点,主要有如下方法:</p>
<ul><li>setStart(): 设置 Range 的
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP