前端 富文本编辑器原理——从javascript、html、css开始入门
文章目录
- ⭐前言
- ⭐html的contenteditable属性
- 💖 输入的光标位置(浏览器获取selection)
- ⭐使用Selection.toString () 返回指定的文本
- ⭐getRangeAt 获取指定索引范围
- 💖 修改光标位置
- 💖 设置选取range
- ⭐总结
- ⭐结束

⭐前言
大家好,我是yma16,本文分享关于前端 富文本编辑器原理——从javascript、html、css开始。
富文本编辑器
富文本编辑器是指具有格式化文本和图像编辑功能的文本编辑器
参考文档:https://w3c.github.io/selection-api/#abstract
⭐html的contenteditable属性
全局属性 contenteditable 是一个枚举属性,表示元素是否可被用户编辑。如果可以,浏览器会修改元素的组件以允许编辑。
- contenteditable boolean (false | true) 默认false
简单理解,加上contenteditable ,html可以编辑具有input 输入的基本功能,所见即所得。
例:
html demo 标签配置contenteditable
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>富文本编辑器</title></head><style>blockquote {background: #eee;border-radius: 5px;margin: 16px 0;}blockquote p {padding: 15px;}cite {margin: 16px 32px;font-weight: bold;}blockquote p::before {content: '\201C';}blockquote p::after {content: '\201D';}[contenteditable='true'] {caret-color: red;}</style><body><blockquote contenteditable="true"><p>Edit this content to add your own quote</p></blockquote><cite contenteditable="true">-- Write your own name here</cite></body>
</html>
效果如下,可以输入编辑html元素:
💖 输入的光标位置(浏览器获取selection)
getSelection() method
GetSelection ()方法返回一个 Selection 对象,该对象表示用户选择的文本范围或插入符号的当前位置。
⭐使用Selection.toString () 返回指定的文本
例:
<body><blockquote contenteditable="true"><p>Edit this content to add your own quote</p></blockquote><cite contenteditable="true">-- Write your own name here</cite><button onclick="printSelection()">console.log(getSelection)</button></body>
<script type="text/javascript">const printSelection=()=>{const selection=window.getSelection()console.log('selection',selection)console.log('selection.toString()',selection.toString())}
</script>
效果如下:
⭐getRangeAt 获取指定索引范围
函数接受一个索引值
返回,其中
结束的索引值,endOffset
开始的索引值,startOffset
效果如下图:
💖 修改光标位置
调用 setStart() 和 setEnd() 方法,来修改一个光标的位置或拖蓝范围
Range.setStart()
Range.setStart() 方法用于设置 Range的开始位置。
如果起始节点类型是 Text、Comment 或 CDATASection之一,那么 startOffset 指的是从起始节点算起字符的偏移量。对于其他 Node 类型节点,startOffset 是指从起始结点开始算起子节点的偏移量。
如果设置的起始位点在结束点之下(在文档中的位置),将会导致选区折叠,起始点和结束点都会被设置为指定的起始位置。
startNode
startNode 用于设定 Range的起始位置。
startOffset
必须为不小于 0 的整数。表示从startNode的开始位置算起的偏移量。
Range.setEnd()
Range.setEnd()
方法用于设置 Range的结束位置。
如果结束节点类型是 Text、Comment 或 CDATASection之一,那么 endOffset 指的是从结束节点算起字符的偏移量。对于其他 Node 类型节点,endOffset 是指从结束结点开始算起子节点的偏移量。
如果设置的结束点在起始点之上(在文档中的位置),将会导致选区折叠,起始点和结束点都会被设置为指定的结束位置。
endNode
endNode用于设定 Range的结束位置。
endOffset
必须为不小于 0 的整数。表示从endNode的结束位置算起的偏移量。
语法
range.setStart(startNode, startOffset);
range.setEnd(endNode, endOffset);
例:
点击不失去焦点跳转开始和结束,避免失去焦点使用preventDefault
<body><blockquote contenteditable="true"><p>Edit this content to add your own quote</p></blockquote><cite contenteditable="true">-- Write your own name here</cite><button onclick="printSelection()">console.log(getSelection)</button><button id='start-id'>jumpt start</button><button id='end-id'>jumpt end</button></body>
<script type="text/javascript">function printSelection() {const selection = window.getSelection()console.log('selection', selection)console.log('selection.toString()', selection.toString())console.log('selection.getRangeAt(0)', selection.getRangeAt(0))}function jumpStart() {let range = window.getSelection().getRangeAt(0);let textEle = range.commonAncestorContainer;range.setStart(range.startContainer, 0);range.setEnd(range.startContainer, 0);}function jumpEnd() {let range = window.getSelection().getRangeAt(0);let textEle = range.commonAncestorContainer;range.setStart(range.startContainer, textEle.length);range.setEnd(range.endContainer, textEle.length);}window.onload = function() {document.getElementById('start-id').addEventListener('click', function(e) {jumpStart()})document.getElementById('start-id').addEventListener('mousedown', function(e) {e.preventDefault()})document.getElementById('end-id').addEventListener('click', function(e) {jumpEnd()})document.getElementById('end-id').addEventListener('mousedown', function(e) {e.preventDefault()})}
</script>
效果如下图:
💖 设置选取range
Selection.addRange()
概述
向选区(Selection)中添加一个区域(Range)。
语法
sel.addRange(range)
例子:
失去焦点之后恢复选区
<body><blockquote contenteditable="true"><p>Edit this content to add your own quote</p><b>yma16</b></blockquote><cite contenteditable="true">-- Write your own name here</cite><br><button id='print-id'>console.log(getSelection)</button><br><br><button id='start-id'>jump start</button><button id='end-id'>jump end</button><button id='focus-id'>focus content</button></body>
<script type="text/javascript">const config = {selection: null}function printSelection() {const selection = window.getSelection()range = document.getSelection().getRangeAt(0).cloneRange();config.cloneRange = range;console.log('selection', selection)// console.log('selection.toString()', selection.toString())// console.log('selection.getRangeAt(0)', selection.getRangeAt(0))}function jumpStart() {let range = window.getSelection().getRangeAt(0);let textEle = range.commonAncestorContainer;range.setStart(range.startContainer, 0);range.setEnd(range.startContainer, 0);range = document.getSelection().getRangeAt(0).cloneRange();config.cloneRange = range;}function jumpEnd() {let range = window.getSelection().getRangeAt(0);let textEle = range.commonAncestorContainer;range.setStart(range.startContainer, textEle.length);range.setEnd(range.endContainer, textEle.length);range = document.getSelection().getRangeAt(textEle.length).cloneRange();config.cloneRange = range;}function focusContent() {document.getSelection().removeAllRanges(); //把没用的Ranges删除console.log('config.cloneRange',config.cloneRange)document.getSelection().addRange(config.cloneRange); //恢复Range}window.onload = function() {document.getElementById('print-id').addEventListener('click', function(e) {printSelection()})document.getElementById('print-id').addEventListener('mousedown', function(e) {e.preventDefault()})document.getElementById('start-id').addEventListener('click', function(e) {jumpStart()})document.getElementById('start-id').addEventListener('mousedown', function(e) {e.preventDefault()})document.getElementById('end-id').addEventListener('click', function(e) {jumpEnd()})document.getElementById('end-id').addEventListener('mousedown', function(e) {e.preventDefault()})document.getElementById('focus-id').addEventListener('click', function(e) {focusContent()})document.getElementById('focus-id').addEventListener('mousedown', function(e) {e.preventDefault()})}
</script>
效果如下:
⭐总结
Contenteditable属性
Contenteditable是一种HTML属性,用于指定页面中的元素是否可以编辑。以下是Contenteditable的总结:
-
Contenteditable属性可以应用于HTML元素上,如div、span、p、h1等等。
-
当Contenteditable属性设置为true时,用户可以编辑元素内的文本、图像等内容。
-
Contenteditable属性的取值包括true和false,分别表示可编辑和不可编辑。
-
Contenteditable属性可以通过JavaScript来动态修改。
-
Contenteditable属性的兼容性很好,支持大多数现代浏览器。
-
虽然Contenteditable属性很方便,但也存在一些潜在的安全问题,因此应该谨慎使用。
富文本编辑器
富文本编辑器是一种允许用户编辑带有样式和格式的文本的编辑器。它与普通文本编辑器的区别在于,它允许用户使用各种字体、颜色、大小、加粗、斜体、下划线、超链接、图片等来设计和呈现文本内容。其原理主要包括以下几个方面:
-
DOM操作:富文本编辑器通过操作DOM树来实现文本样式和格式的改变。当用户在编辑器中输入或选择文本时,编辑器会将文本转换为DOM节点,并将节点添加到DOM树中。通过对DOM节点的增删改查,实现对文本样式和格式的修改操作。
-
事件监听:通常情况下,富文本编辑器会监听用户的输入事件、鼠标点击事件和键盘事件等,以便及时捕捉用户的操作并做出相应的反应。
-
样式和格式化:富文本编辑器通常内置了样式和格式化工具,比如字体、颜色、大小、加粗、斜体、下划线、超链接等,可以通过这些工具来控制文本的样式和格式。
-
插件和组件:富文本编辑器可以通过插件和组件来扩展其功能,比如图片上传组件、表格插件、代码高亮插件等,可以让编辑器满足更多的需求。
⭐结束
本文分享到这结束,如有错误或者不足之处欢迎指出!
👍 点赞,是我创作的动力!
⭐️ 收藏,是我努力的方向!
✏️ 评论,是我进步的财富!
💖 感谢你的阅读!
相关文章:

前端 富文本编辑器原理——从javascript、html、css开始入门
文章目录 ⭐前言⭐html的contenteditable属性💖 输入的光标位置(浏览器获取selection)⭐使用Selection.toString () 返回指定的文本⭐getRangeAt 获取指定索引范围 💖 修改光标位置💖 设置选取range ⭐总结⭐结束 ⭐前…...

堆--数据流中第K大元素
如果对于堆不是太认识,请点击:堆的初步认识-CSDN博客 数据流与上述堆--数组中第K大元素-CSDN博客的数组区别: 数据流的数据是动态变化的,数组是写死的 堆--数组中第K大元素-CSDN博客题的小顶堆加一个方法: class MinH…...

【算法|动态规划No.12】leetcode152. 乘积最大子数组
个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 🍔本专栏旨在提高自己算法能力的同时,记录一下自己的学习过程,希望…...

Covert Communication 与选择波束(毫米波,大规模MIMO,可重构全息表面)
Covert Communication for Spatially Sparse mmWave Massive MIMO Channels 2023 TOC abstract 隐蔽通信,也称为低检测概率通信,旨在为合法用户提供可靠的通信,并防止任何其他用户检测到合法通信的发生。出于下一代通信系统安全链路的强烈…...

计算机毕业设计 基于协调过滤算法的绿色食品推荐系统的设计与实现 Java实战项目 附源码+文档+视频讲解
博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精…...

华为云云耀云服务器L实例评测|部署在线影音媒体系统 Jellyfin
华为云云耀云服务器L实例评测|部署在线影音媒体系统 Jellyfin 一、云耀云服务器L实例介绍1.1 云服务器介绍1.2 产品规格1.3 应用场景1.4 支持镜像 二、云耀云服务器L实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 Jellyfin3.1 Jellyfin 介绍3.2 Docke…...

GhostNet原理解析及pytorch实现
论文:https://arxiv.org/abs/1911.11907 源码:https://github.com/huawei-noah/ghostnet 简要论述GhostNet的核心内容。 Ghost Net 1、Introduction 在训练良好的深度神经网络的特征图中,丰富甚至冗余的信息通常保证了对输入数据的全面理…...

视频二维码的制作方法,支持内容修改编辑
现在学生经常会需要使用音视频二维码,比如外出打开、才艺展示、课文背诵等等。那么如何制作一个可以长期使用的二维码呢?下面来给大家分享一个二维码制作(免费在线二维码生成器-二维码在线制作-音视频二维码在线生成工具-机智熊二维码&#x…...
清华GLM部署记录
环境部署 首先安装anaconda(建议包管理比较方便)windows用户需手动配置一下环境变量,下面默认是在ubuntu环境说明创建python环境,conda create -n your_env_name python3.10 (注:官方是提供是python3.8,但…...

贪心算法+练习
正值国庆之际,祝愿祖国繁荣昌盛,祝愿朋友一生平安!终身学习,奋斗不息! 目录 1.贪心算法简介 2.贪心算法的特点 3.如何学习贪心算法 题目练习(持续更新) 1.柠檬水找零(easy&…...

使用华为eNSP组网试验⑷-OSPF多区域组网
今天进行了OSPF的多区域组网试验,本来这是个很简单的操作,折腾了好长时间,根本原因只是看了别人写的配置代码,没有真正弄明白里面对应的规则。 一般情况下,很多单位都使用OSPF进行多区域的组网,大体分为1个…...
P1843 奶牛晒衣服 【贪心】
P1843 奶牛晒衣服 【贪心】 题目背景 熊大妈决定给每个牛宝宝都穿上可爱的婴儿装 。但是由于衣服很湿,为牛宝宝晒衣服就成了很不爽的事情。于是,熊大妈请你(奶牛)帮助她完成这个重任。 题目描述 一件衣服在自然条件下用一秒的时间…...

91、Redis - 事务 与 订阅-发布 相关的命令 及 演示
★ 事务相关的命令 Redis事务保证事务内的多条命令会按顺序作为整体执行,其他客户端发出的请求绝不可能被插入到事务处理的中间, 这样可以保证事务内所有命令作为一个隔离操作被执行。 Redis事务同样具有原子性,事务内所有命令要么全部被执…...

GPU如何成为AI的加速器
0. 前言 按照国际惯例,首先声明:本文只是我自己学习的理解,虽然参考了他人的宝贵见解,但是内容可能存在不准确的地方。如果发现文中错误,希望批评指正,共同进步。 本文关键词:GPU、深度学习、GP…...
Map声明、元素访问及遍历、⼯⼚模式、实现 Set - GO语言从入门到实战
Map声明、元素访问及遍历 - GO语言从入门到实战 Map 声明的方式 m := map[string]int{"one": 1, "two": 2, "three": 3} //m初始化时就已经设置了3个键值对,所以它的初始长度len(m)是3。m1 := map[string]int{} //m1被初始化为一个空的m…...

机器人中的数值优化|【七】线性搜索牛顿共轭梯度法、可信域牛顿共轭梯度法
机器人中的数值优化|【七】线性搜索牛顿共轭梯度法、可信域牛顿共轭梯度法 Line Search Newton-CG, Trust Region Newton-CG 往期回顾 机器人中的数值优化|【一】数值优化基础 机器人中的数值优化|【二】最速下降法,可行牛顿法的python实现,以Rosenbro…...

websocket实现go(server)与c#(client)通讯
go 服务端 使用到github.com/gorilla/websocket package mainimport ("fmt""github.com/gorilla/websocket""log""net/http" )func main() {var upgrader websocket.Upgrader{ReadBufferSize: 1024,WriteBufferSize: 1024,CheckOr…...

洛谷题目题解详细解答
洛谷是一个很不错的刷题软件,可是找不到合适的题解是个大麻烦,大家有啥可以私信问我,以下是我已经通过的题目。 你如果有哪一题不会(最好是我通过过的,我没过的也没关系),可以私信我࿰…...

【C语言】八大排序算法
文章目录 一、冒泡排序1、定义2、思想及图解3、代码 二、快速排序1、hoare版本2、挖坑法3、前后指针法4、非递归快排5、快速排序优化1)三数取中选key值2)小区间优化 三、直接插入排序1、定义2、代码 四、希尔排序1、定义2、图解3、代码 五、选择排序1、排…...

2023年中国智能电视柜产量、需求量、市场规模及行业价格走势[图]
电视柜是随着电视机的发展和普及而演变出的家具种类,其主要作用是承载电视机,又称视听柜,随着生活水平的提高,与电视机相配套的电器设备也成为电视柜的收纳对象。 随着智能家具的发展,智能电视机柜的造型和风格都是有了…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...

基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...