前端常用的输入文本内容便是input与textarea,但是有一些他们有一些局限性,比如input只能输入一行,textarea虽然可以通过设置rows
属性来动态调整高度,但是他们都不能存在多种格式的内容,更不能有标签一样样式了。
浏览器对于一些dom元素有一个属性contenteditable可以快速将该元素变成可编辑的输入框
contenteditable的使用
<div id="myInput" contenteditable="true">
这是一个子段落
</div>
在上面的情况下,继续输入,再按下回车换行的时候会在下面生成div标签,如下图(这里是在p标签下增加的contenteditable属性),这种情况也引申出该方式的缺陷——结构混乱,不同浏览器之间表现可能会有所不同,具体缺陷看下文
上图能看出,换行是通过插入div标签来实现的,不过不用这种方式,还可以通过document.execCommand("insertLineBreak")命令来插入br来取代分割div标签(注意document.execCommand API也是快废弃的命令了,会大大影响产品的稳定性、扩展性)
元素的数据及复制粘贴
<div id="myInput" contenteditable="true"
dangerouslySetInnerHTML={{_html: contextHtml}
>
这是一个子段落
</div>
上面代码React是通过dangerouslySetInnerHTML属性来给它赋值(vue是v-html),所以在复制粘贴的时候会是以标签的形式存在。所以我们如果只粘贴文本的话需要监听粘贴事件并通过方法来解决这个问题。
操作dom
操作dom需要了解
- Window.getSelection Window.getSelection - Web API 接口参考 | MDN
- Selection Selection - Web API 接口参考 | MDN
- Range Range - Web API 接口参考 | MDN
- Window.getSelection返回一个 Selection 对象,表示用户选择的文本范围或光标的当前位置。
- selection 对象表示用户选择的文本范围或插入符号的当前位置。它代表页面中的文本选区,可能横跨多个元素。文本选区由用户拖拽鼠标经过文字而产生。要获取用于检查或修改的 Selection 对象,请调用 window.getSelection()。
- Range 接口表示一个包含节点与文本节点的一部分的文档片段。
下面是一段代码案例:将选取中的部分替换成newContextText
const selection = window.getSelection();
if(selection.rangeCount > 0){
const range = selection.getRangeAt(0);
range.deleteContents()
let fragment = range.createContextualFragment(newContextText); // newContextText为标签字符串
range.insertNode(fragment)
selection.removeAllRanges()
}
获取选区中的内容,节点,光标信息
- 获取选区文本:window.getSelection().toString() 可以获取选取的文本,注意如果没有window.getSelection()会报错
- 获取选区节点: range.startContainer开始节点
- 获取光标信息
contenteditable的缺陷
-
样式难以控制:使用 contentEditable 属性时,用户可以自由编辑文本内容,这可能会导致样式混乱或不一致,尤其在不同浏览器之间表现可能会有所不同。
-
操作dom困难,通过选区range操作选中区域进行修改元素来改变样式,会使编辑器内部元素结构混乱,没有一套标准,或者这套标准会一些缺陷
-
安全性问题:开启 contentEditable 属性可能会带来安全隐患,因为用户可以插入不安全的脚本代码或其他恶意内容,从而导致跨站脚本攻击(XSS)等安全漏洞。
-
兼容性问题:不同浏览器对 contentEditable 属性的支持程度各不相同,可能会导致页面在不同浏览器上表现不一致或出现兼容性问题。
-
性能问题:当页面中有大量元素设置为可编辑时,可能会影响页面的性能,导致页面加载缓慢或卡顿。
-
可访问性问题:对于一些特殊用户群体(如视力受损或使用辅助技术的用户),contentEditable 可能会影响页面的可访问性,导致他们无法正常使用页面。
推荐使用
市面上的很多编辑器其实就是基于contenteditable的属性封装改编的。比如wangEditor老版本(v4版本)就是基于contenteditable的,推荐现在使用wangEditor新版本(v5),新版本废弃了document.execCommand
API ,使用 slate.js (open in new window)(但不依赖 React)为内核,这也是目前主流富文本编辑器的技术方案。