高级JavaScript。如何用JavaScript手撸一个富文本编辑器?

要素过多建议收藏


- 富文本编辑

基本的技术就是在空白 HTML 文件中嵌入一个
iframe 。通过 designMode 属性,可以将这个空白文档变成可以编辑的,实际编辑的则是 <body> 元素
HTML designMode 属性有两个可能的值: "off" (默认值)和 "on" 。设置为 "on" 时,整个文档
都会变成可以编辑的(显示插入光标),从而可以像使用文字处理程序一样编辑文本,通过键盘将文本
标记为粗体、斜体,等等。
作为 iframe 源的是一个非常简单的空白 HTML 页面。下面是一个例子:
<!DOCTYPE html>
<html><head><title>Blank Page for Rich Text Editing</title></head><body></body>
</html>
这个页面会像其他任何页面一样加载到 iframe 里。为了可以编辑,必须将文档的 designMode
性设置为 "on" 。不过,只有在文档完全加载之后才可以设置。在这个包含页面内,需要使用 onload
事件处理程序在适当时机设置 designMode ,如下面的例子所示:
<iframe name="richedit" style="height: 100px; width: 100px"></iframe> 
<script> window.addEventListener("load", () => { frames["richedit"].document.designMode = "on"; }); 
</script>
以上代码加载之后,可以在页面上看到一个类似文本框的区域。这个框的样式具有网页默认样式,
不过可以通过 CSS 调整。

- 使用 contenteditable

还有一种处理富文本的方式,也是 IE 最早实现的,即指定 contenteditable 属性。可以给页面
中的任何元素指定 contenteditable 属性,然后该元素会立即被用户编辑。这种方式更受欢迎,因为
不需要额外的 iframe 、空页面和 JavaScript ,只给元素添加一个 contenteditable 属性即可,比如:
<div class="editable" id="richedit" contenteditable ></div>
元素中包含的任何文本都会自动被编辑,元素本身类似于 <textarea> 元素。通过设置
contentEditable 属性,也可以随时切换元素的可编辑状态:
let div = document.getElementById("richedit");
richedit.contentEditable = "true";
contentEditable 属性有 3 个可能的值: "true" 表示开启, "false" 表示关闭, "inherit" 表示
继承父元素的设置(因为在 contenteditable 元素内部会创建和删除元素)。 IE Firefox Chrome
Safari Opera 及所有主流移动浏览器都支持 contentEditable 属性。

- 与富文本交互

与富文本编辑器交互的主要方法是使用 document.execCommand() 。这个方法在文档上执行既定
的命令,可以实现大多数格式化任务。 document.execCommand() 可以接收 3 个参数:要执行的命令、
表示浏览器是否为命令提供用户界面的布尔值和执行命令必需的值(如果不需要则为 null )。为跨浏览
器兼容,第二个参数应该始终为 false ,因为 Firefox 会在其为 true 时抛出错误。
不同浏览器支持的命令也不一样。下表列出了最常用的命令。

 

剪贴板相关的命令与浏览器关系密切。虽然这些命令并不都可以通过 document.execCommand()
使用,但相应的键盘快捷键都是可以用的。
这些命令可以用于修改内嵌窗格( iframe )中富文本区域的外观,如下面的例子所示:
// 在内嵌窗格中切换粗体文本样式
frames["richedit"].document.execCommand("bold", false, null);
// 在内嵌窗格中切换斜体文本样式
frames["richedit"].document.execCommand("italic", false, null);
// 在内嵌窗格中创建指向 www.wrox.com 的链接
frames["richedit"].document.execCommand("createlink", false,
"http://www.wrox.com");
// 在内嵌窗格中为内容添加 <h1> 标签
frames["richedit"].document.execCommand("formatblock", false, "<h1>");
同样的方法也可以用于页面中添加了 contenteditable 属性的元素,只不过要使用当前窗口而不
是内嵌窗格中的 document 对象:
// 切换粗体文本样式
document.execCommand("bold", false, null);
// 切换斜体文本样式
document.execCommand("italic", false, null);
// 创建指向 www.wrox.com 的链接
document.execCommand("createlink", false, "http://www.wrox.com");
// 为内容添加 <h1> 标签
document.execCommand("formatblock", false, "<h1>");

 

注意,即使命令是所有浏览器都支持的,命令生成的 HTML 通常差别也很大。例如,为选中文本
应用 bold 命令在 IE Opera 中会使用 <strong> 标签,在 Safari Chrome 中会使用 <b>标签,而在Firefox 中会使用 <span> 标签。在富文本编辑中,不能依赖浏览器生成的 HTML,因为命令实现和格式 转换都是通过 innerHTML 完成的。
还有与命令相关的其他一些方法。第一个方法是 queryCommandEnabled() ,此方法用于确定对当
前选中文本或光标所在位置是否可以执行相关命令。它只接收一个参数,即要检查的命令名。如果可编 辑区可以执行该命令就返回 true ,否则返回 false 。来看下面的例子:
let result = frames["richedit"].document.queryCommandEnabled("bold");
以上代码在当前选区可以执行 "bold" 命令时返回 true 。不过要注意, queryCommandEnabled()
返回 true 并不代表允许执行相关命令,只代表当前选区适合执行相关命令。在 Firefox 中,
queryCommandEnabled("cut") 即使默认不允许剪切也会返回 true
另一个方法 queryCommandState() 用于确定相关命令是否应用到了当前文本选区。例如,要确定
当前选区的文本是否为粗体,可以这样:
let isBold = frames["richedit"].document.queryCommandState("bold");
如果之前给文本选区应用过 "bold" 命令,则以上代码返回 true 。全功能富文本编辑器可以利用这
个方法更新粗体、斜体等按钮。
最后一个方法是 queryCommandValue() ,此方法可以返回执行命令时使用的值(即前面示例的
execCommand() 中的第三个参数)。如果对一段选中文本应用了值为 7 "fontsize"命令,则如下代 码会返回 7
let fontSize = frames["richedit"].document.queryCommandValue("fontsize");
这个方法可用于确定如何将命令应用于文本选区,从而进一步决定是否需要执行下一个命令。

- 富文件选择 

在内嵌窗格中使用 getSelection() 方法,可以获得富文本编辑器的选区。这个方法暴露在
document window 对象上,返回表示当前选中文本的 Selection 对象。每个 Selection 对象都拥
有以下属性。
anchorNode :选区开始的节点。
anchorOffset :在 anchorNode 中,从开头到选区开始跳过的字符数。
focusNode :选区结束的节点。
focusOffset focusNode 中包含在选区内的字符数。
isCollapsed :布尔值,表示选区起点和终点是否在同一个地方。
rangeCount :选区中包含的 DOM 范围数量。
Selection 的属性并没有包含很多有用的信息。好在它的以下方法提供了更多信息,并允许操作
选区。
addRange( range ) :把给定的 DOM 范围添加到选区。
collapse( node, offset ) :将选区折叠到给定节点中给定的文本偏移处。
collapseToEnd() :将选区折叠到终点。
collapseToStart() :将选区折叠到起点。
containsNode( node ) :确定给定节点是否包含在选区中。
deleteFromDocument() :从文档中删除选区文本。与执行 execCommand("delete", false,
null) 命令结果相同。
extend( node, offset ) :通过将 focusNode focusOffset 移动到指定值来扩展选区。
getRangeAt( index ) :返回选区中指定索引处的 DOM 范围。
removeAllRanges() :从选区中移除所有 DOM 范围。这实际上会移除选区,因为选区中至少
要包含一个范围。
removeRange( range ) :从选区中移除指定的 DOM 范围。
selectAllChildren( node ) :清除选区并选择给定节点的所有子节点。
toString() :返回选区中的文本内容。
Selection 对象的这个方法极其强大,充分利用了 DOM 范围来管理选区。操纵 DOM 范围可以实
现比 execCommand() 更细粒度的控制,因为可以直接对选中文本的 DOM 内容进行操作。来看下面的 例子:
let selection = frames["richedit"].getSelection(); 
// 取得选中的文本
let selectedText = selection.toString(); 
// 取得表示选区的范围
let range = selection.getRangeAt(0); 
// 高亮选中的文本
let span = frames["richedit"].document.createElement("span"); 
span.style.backgroundColor = "yellow"; 
range.surroundContents(span);
以上代码会在富文本编辑器中给选中文本添加黄色高亮背景。实现方式是在默认选区使用 DOM
围,用 surroundContents() 方法给选中文本添加背景为黄色的 <span> 标签。
getSelection() 方法在 HTML5 中进行了标准化, IE9 以及 Firefox Safari Chrome Opera 的所
有现代版本中都实现了这个方法。
IE8 及更早版本不支持 DOM 范围,不过它们允许通过专有的 selection 对象操作选中的文本。如
本章前面所讨论的,这个 selection 对象是 document 的属性。要取得富文本编辑器中选中的文本, 必须先创建一个文本范围,然后再访问其 text 属性:
let range = frames["richedit"].document.selection.createRange(); 
let selectedText = range.text;
使用 IE 文本范围执行 HTML 操作不像使用 DOM 范围那么可靠,不过也是可以做到的。要实现与
使用 DOM 范围一样的高亮效果,可以组合使用 htmlText 属性和 pasteHTML() 方法:
let range = frames["richedit"].document.selection.createRange(); 
range.pasteHTML( '<span style="background-color:yellow">${range.htmlText}</span>');
以上代码使用 htmlText 取得了当前选区的 HTML ,然后用一个 <span> 标签将其包围起来并通过
pasteHTML() 再把它插入选区中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/618315.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高级RAG(六): 句子-窗口检索

之前我们介绍了LlamaIndex的从小到大的检索 的检索方法&#xff0c;今天我们再来介绍llamaindex的另外一种高级检索方法: 句子-窗口检索(Sentence Window Retrieval)&#xff0c;在开始介绍之前让我们先回顾一下基本的RAG检索的流程&#xff0c;如下图所示&#xff1a; 在执行基…

Fedora 36 正式发布稳定的Linux桌面版本

Fedora 36今天发布&#xff0c;这是最近一段时间以来又一个强大、前沿而又稳定可靠的Linux发行版本&#xff0c;除了这些特点外&#xff0c;Fedora 36还在原先的基础上增加了新的功能和细节打磨。 Fedora 36使用GNOME 42作为其默认的Fedora工作站桌面环境。 OpenSSL 3.0&#x…

用React给XXL-JOB开发一个新皮肤(三):实现登录页和Layout骨架

目录 一. 简述二. 接口服务调整 2.1. 登录接口2.2. 登出接口2.3. 修改密码接口2.4. 修改配置文件 三. 前端HTTP 请求四. 登录页面 4.1. 搭建登录页面4.2. 对接登录接口 五. Layout 骨架 5.1. 搭建骨架5.2. Header5.3. 修改密码5.4. 退出登录 六. 其他 一. 简述 上一篇文章我…

视频监控平台的管理员账号在所有客户端都无法登录的问题解决

目 录 一、问题描述 二、问题排查 1、看问题提示 2、看日志信息 3、问题定位 三、问题解决 1. 添加权限角色 2、添加操作用户 3、验证 一、问题描述 AS-V1000视频监控平台安装部署完成后&#xff0c;发现管理员admin不能到web客户端&#xff0c;觉…

D25XB100-ASEMI家用电器整流桥D25XB100

编辑&#xff1a;ll D25XB100-ASEMI家用电器整流桥D25XB100 型号&#xff1a;D25XB100 品牌&#xff1a;ASEMI 封装&#xff1a;GBJ-5&#xff08;带康铜丝&#xff09; 平均正向整流电流&#xff08;Id&#xff09;&#xff1a;25A 最大反向击穿电压&#xff08;VRM&…

AcWing 103. 电影(map、pair连用or离散化)

题目 方法一&#xff08;mappair&#xff09; 其实上面这么长巴拉巴拉就是在说 首先&#xff0c;每个科学家会的语言都不同。但是呢每部电影的字幕和语言是不一样的&#xff08;字幕和语言一定不相同&#xff09; 要求找到一部电影使得在场能听懂的科学家最多&#xff08;如果存…

Linux Kernel Stack Overflow/Linux 内核栈溢出

不同于Linux应用程序的栈能够动态增长&#xff0c;Linux内核栈是固定的&#xff0c;并且比较小&#xff0c;比如Linux 2.6.x内核&#xff0c;在X86 32位架构上一般是4K或8K&#xff08;在进行内核编译时&#xff0c;Kernel hacking下进行配置&#xff0c;默认8K&#xff09;&am…

上门回收小程序开发,让回收更加简单

资源回收一直是当下深受大众关注的话题&#xff0c;如何做到资源不浪费&#xff0c;成为了大众要考虑的问题。在人们环保意识的加深下&#xff0c;回收行业也是获得了大众的关注&#xff0c;逐渐形成了一个新的商业模式。 随着互联网技术的发展&#xff0c;回收行业也更加方便…

C/S架构,集成三维影像后处理功能,自主版权的一套医院PACS系统源码

一、PACS简介 PACS&#xff08;PictureArchivingandCommunicationsSystem&#xff09;即图像存储与传输系统&#xff0c;是应用于医院的数字医疗设备如CT、MR&#xff08;磁共振&#xff09;、US&#xff08;超声成像&#xff09;、X光机、DSA&#xff08;数字减影&#xff09…

KIBANA可视化管理界面说明

更说明转自https://blog.csdn.net/IT_ZRS/article/details/125496588 1 主要结构功能 使用浏览器访问 ip:5601 默认端口&#xff0c;进入首页 Discover&#xff1a;日志管理视图 主要进行搜索和查询Visualize&#xff1a;统计视图 构建可视化的图表Dashboard&#xf…

【目标检测】YOLOv5算法实现(八):模型验证

本系列文章记录本人硕士阶段YOLO系列目标检测算法自学及其代码实现的过程。其中算法具体实现借鉴于ultralytics YOLO源码Github&#xff0c;删减了源码中部分内容&#xff0c;满足个人科研需求。   本系列文章主要以YOLOv5为例完成算法的实现&#xff0c;后续修改、增加相关模…

JavaWeb,CSS的学习

CSS&#xff0c;层叠样式表&#xff08;Cascading Style Sheets&#xff09;&#xff0c;能够对网页中元素位置的排版进行像素级精确控制&#xff0c;支持几乎所有的字体字号样式&#xff0c;拥有网页对象和模型样式编辑的能力&#xff0c;简单来说&#xff0c;美化页面。 CSS…

c++临时对象的探讨及相关性能提升

产生临时对象的情况 我们定义一个类进行测试 class tempVal { public:int v1, v2;tempVal(int v1 0, int v2 0);tempVal(const tempVal& t) :v1(t.v1), v2(t.v2) {cout << "调用拷贝构造函数" << endl;}virtual ~tempVal() {cout << "…

【python】——turtle动态画

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

AR HUD全面「上新」

AR HUD赛道正在迎来新的时代。 上周&#xff0c;蔚来ET9正式发布亮相&#xff0c;新车定位为D级行政旗舰轿车&#xff0c;其中&#xff0c;在智能座舱交互层面&#xff0c;继理想L系列、长安深蓝S7之后&#xff0c;也首次取消仪表盘&#xff0c;取而代之的是业内首个全焦段AR H…

分块矩阵的定义、计算

目录 一、定义 二、分块矩阵的加减乘法 三、考点 一、定义 分块&#xff0c;顾名思义&#xff0c;将整个矩阵分成几部分&#xff0c;如下图所示 二、分块矩阵的加减乘法 三、考点 分块矩阵的考点不多&#xff0c;一般来说&#xff0c;有一种&#xff1a; 求分块矩阵的转置…

PHP如何拆分中文名字(包括少数民族名字)

/*** param string|null $name* return array|null*/ function splitName($name) {if (empty($name) || empty(trim($name))) {return null;}//该正则是用来提取$name参数里面的中文字符的。preg_match_all(/[\x{4e00}-\x{9fff}]/u, $name, $matchers);$matchersCount isset($…

2024年,谷歌云首席技术官眼中的生成AI三大支柱,来看看有啥新花样

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

App在线封装的革命性创新

随着移动互联网的蓬勃发展&#xff0c;App已经成为我们日常生活中不可或缺的一部分。从购物、交通、社交到娱乐&#xff0c;几乎每个人的智能手机都装载着数十个应用程序&#xff0c;以满足各式各样的需求。然而&#xff0c;对于许多非技术背景的企业家和小型企业而言&#xff…

【机器学习】模型参数优化工具:Optuna使用分步指南(附XGB/LGBM调优代码)

常用的调参方式和工具包 常用的调参方式包括网格搜索(Grid Search)、**随机搜索(Random Search)和贝叶斯优化(Bayesian Optimization)**等。 工具包方面&#xff0c;Scikit-learn提供了GridSearchCV和RandomizedSearchCV等用于网格搜索和随机搜索的工具。另外&#xff0c;有一…