前端 富文本编辑器原理——从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年中国智能电视柜产量、需求量、市场规模及行业价格走势[图]
电视柜是随着电视机的发展和普及而演变出的家具种类,其主要作用是承载电视机,又称视听柜,随着生活水平的提高,与电视机相配套的电器设备也成为电视柜的收纳对象。 随着智能家具的发展,智能电视机柜的造型和风格都是有了…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...
