这个笔记来自网络资料的总结

简书大佬三省吾身_9862

tuobaye个人博客

富文本有相关3个API和一个新属性

  • var selection = window.getSelection();
  • var range = selection.getRangeAt;
  • document.execCommand(x,y,z);
  • 新属性 contenteditable="true"

第一步

<!-- 让标签变成可输入的区域 -->
<div contenteditable="true" id="rich-editor">

selection 对象和 range 对象来操作光标

通常情况下我们不会直接操作 selection 对象,而是需要操作用 seleciton 对象所对应的用户选择的 ranges (区域),俗称拖蓝

var selection = window.getSelection();
var range = selection.getRangeAt(0);
// 由于浏览器当前可能存在多个文本选取,所以 getRangeAt 函数接受一个索引值。在富文本编辑其中,我们不考虑多选取的可能性,就当上面是固定写法

这是光标位置的打印,因为光标和console不能同时操作,所以用了计时器

这是拖蓝位置的打印,因为光标和console不能同时操作,所以用了计时器

上面两个图的的属性

  • startContainer: range 范围的起始节点。
  • endContainer: range 范围的结束节点
  • startOffset: range 起点位置的偏移量。
  • endOffset: range 终点位置的偏移量。
  • commonAncestorContainer: 返回包含 startContainer 和 endContainer 的最深的节点。
  • collapsed: 返回一个用于判断 Range 起始位置和终止位置是否相同的布尔值。

selection的方法有

selection.getRangeAt()
selection.removeAllRanges()
selection.addRange()

range的方法有

range.setStart(): 设置 Range 的起点
range.setEnd(): 设置 Range 的终点
range.selectNode(): 设定一个包含节点和节点内容的 Range
range.collapse(): 向指定端点折叠该 Range
range.insertNode(): 在 Range 的起点处插入节点。
range.cloneRange(): 返回拥有和原 Range 相同端点的克隆 Range 对象

所以富文本编辑器的基本操作就是,删除原有的 ranges 来保证光标一定会变动到我们想要的位置

let selection  =  window.getSelection();
selection.removeAllRanges();
let range = document.createRange();
range.setStart(startContainer,startOffset);
range.setEnd(endContainer,endOffset);
selection.addRange(range);

对文本的操作

// 一句代码实现富文本编辑器的核心
document.execCommand(x,y,z) // x参数
// 有选中文字,加粗,下划线,删除线,斜体
// 有有当前光标所在行,左 居中 右 两端
// 有有光标所在行, 右缩进 左缩进
// 有有插入,有序列表,无序列表
// 有有选中文字变成,上标 下标
// 有有,全选 复制 剪切
// 有有选中字体,字体大小
// 有有选中字体,字体风格
// 有有选择字体,字体颜色 // y参数,就是false // z参数是需要选中的功能传进来的value

具体查看上面第二个大佬的代码

》》还需要在这上面添加几个功能,修几个bug

插入 ul 和 ol 位置错误

当我们调用 document.execCommand("insertUnorderedList", false, null) 来插入一个列表的时候,新的列表会被插入

标签中,为此我们需要每次调用该命令前做一次修正,参考代码如下:

function  adjustList(){
let lists = document.querySelectorAll("ol, ul");
for(let i = 0;i < lists.length;i++) {
let ele = lists[i]; // ol
let parentNode = ele.parentNode;
if(parentNode.tagName === 'P' && parentNode.lastChild === parentNode.firstChild){
parentNode.insertAdjacentElement('beforebegin',ele);
parentNode.remove()
}
}
}

插入分割线

调用 document.execCommand('insertHorizontalRule', false, null); 会插入一个<hr>标签

光标和<hr>的效果一致了。为此要判断当前光标是否在<li>里面,如果是则在<hr>后面追加一个空的文本节点 #text 不是的话追加 <p><br></p>。然后将光标定位在里面,可用如下方式查找。

function  findParentByTagName(root, name){
let parent=root;
if (typeof name === "string"){
name = [name];
}
while(name.indexOf(parent.nodeName.toLowerCase())===-1&&parent.nodeName!=="BODY"&&parent.nodeName!=="HTML"){
parent=parent.parentNode;
}
return parent.nodeName === "BODY" || parent.nodeName === "HTML"?null:parent;
}

插入链接

调用 document.execCommand('createLink', false, url); 方法我们可以插入一个 url 链接,但是该方法不支持插入指定文字的链接。同时对已经有链接的位置可以反复插入新的链接。为此我们需要重写此方法。

function  insertLink(url,title){
let selection = document.getSelection(),
range = selection.getRangeAt(0);
if(range.collapsed){
let start = range.startContainer,
parent=Util.findParentByTagName(start,'a');
if(parent){
parent.setAttribute('src',url);
}else{
this.insertHTML(`<a href="${url}">${title}</a>`);
}
}else{
document.execCommand('createLink',false,url);
}
}

处理 paste 粘贴

在富文本编辑器中,粘贴效果默认采用如下规则:

  1. 如果是带有格式的文本,则保留格式(格式会被转换成html标签的形式)
  2. 粘贴图文混排的内容,图片可以显示,src 为图片真实地址。
  3. 通过复制图片来进行粘贴的时候,不能粘入内容
  4. 粘贴其他格式内容,不能粘入内容

为了能够控制粘贴的内容,我们监听 paste 事件。该事件的 event 对象中会包含一个 clipboardData 剪切板对象。

对于规则一和规则二,我们可以利用该对象的 getData 方法来获得带有格式和不带格式的内容

// 先判断格式
let plainText = event.clipboardData.getData('text/plain'); // 无格式文本
let plainHTML = event.clipboardData.getData('text/html'); // 有格式文本
// 再选择调用
document.execCommand('insertText', false, plainText);
// 或
document.execCommand('insertHTML', false, plainHTML);
来重写编辑上的paste效果。

然而对于规则 3 ,上述方案就无法处理了。这里我们要引入 event.clipboardData.items,这是一个数组包含了所有剪切板中的内容对象,当你复制了一张图片来粘贴,那么 event.clipboardData.items 的长度就为二

// items[0] 为图片的名称
// items[0].kind 为 string
// items[0].type 为 text/plain 或 text/html
// 获取内容方式如下:
items[0].getAsString(str => {
// 处理 str 即可
}) // items[1] 为图片的二进制数据
// items[1].kind 为 file
// items[1].type 为 图片的格式
// 想要获取里面的内容,我们就需要创建 FileReader 对象了,示例代码如下: let file = items[1].getAsFile();
// file.size 为文件大小
let reader = new FileReader();
reader.onload = function() {
// reader.result 为文件内容,就可以做上传操作了
} if(/image/.test(item.type)) {
reader.readAsDataURL(file); // 读取为 base64 格式
}

插入图片失败

document.execCommand('insertImage', 'false', url);

如果编辑器失去了焦点,那么 selection 和 range 对象将销毁。因此调用 insertImage 时,并不能获得光标所在位置,因此失败。为此需要增加,backupRange() 和 restoreRange() 函数。当页面失去焦点的时候记录 range 信息,插入图片前恢复 range 信息

// 需要在光标离开时记录位置,使用的是最上面的两句代码
var selection = window.getSelection();
var range = selection.getRangeAt(0); // 当用户选择了图片文件后,执行fous()
document.querySelector("#rich-editor").focus() // 然后移动光标到之前的位置
// 再执行
document.execCommand('insertImage', 'false', url);

富文本API的更多相关文章

  1. 图解DevExpress RichEditControl富文本的使用,附源码及官方API

    9点半了,刚写到1.2.   该回家了,明天继续写完. 大家还需要什么操作,留言说一下,没有的我明天继续加. 好久没有玩DevExpress了,今天下载了一个玩玩,发现竟然更新到14.2.5了..我去 ...

  2. Selenium常用API用法示例集----下拉框、文本域及富文本框、弹窗、JS、frame、文件上传和下载

    元素识别方法.一组元素定位.鼠标操作.多窗口处理.下拉框.文本域及富文本框.弹窗.JS.frame.文件上传和下载 元素识别方法: driver.find_element_by_id() driver ...

  3. 富文本编辑器Simditor的简易使用

    最近打算自己做一个博客系统,并不打算使用帝国cms或者wordpress之类的做后台管理!自己处于学习阶段也就想把从前台到后台一起谢了.好了,废话不多说了,先来看看富文本编辑器SimDitor,这里是 ...

  4. iOS富文本

    背景:前些天突然想做一个笔记本功能,一开始,觉得挺简单的呀,一个UITextView,网络缓存也不干了,直接本地NSUserDefault存储,然后完事了,美工,弄几张好看的图片,加几个动画,也就这样 ...

  5. selenium 富文本框处理

    selenium 富文本框处理, 网上有用API的解决方法1:参见:http://blog.csdn.net/xc5683/article/details/8963621 群里1位群友的解决方法2:参 ...

  6. Django集成百度富文本编辑器uEditor

    UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码. 首先从ueEditor官网下载最新版本的包, ...

  7. Quill – 可以灵活自定义的开源的富文本编辑器

    Quill 的建立是为了解决现有的所见即所得(WYSIWYG)的编辑器本身就是所见即所得(指不能再扩张)的问题.如果编辑器不正是你想要的方式,这是很难或不可能对其进行自定义以满足您的需求. Quill ...

  8. iOS - 富文本AttributedString

    最近项目中用到了图文混排,所以就研究了一下iOS中的富文本,打算把研究的结果分享一下,也是对自己学习的一个总结. 在iOS中或者Mac OS X中怎样才能将一个字符串绘制到屏幕上呢?         ...

  9. 富文本常用封装(NSAttributedString浅析)

    最近经常遇到关于富文本的一些需求,特此封装了几个最常用的API分享给大家,但授之以鱼不如授之以渔,接下来会顺便谈谈NSAttributedString,确保你读了本篇文章能够自己封装关于富文本的API ...

随机推荐

  1. Java8新特性——Optional

    前言 在开发中,我们常常需要对一个引用进行判空以防止空指针异常的出现.Java8引入了Optional类,为的就是优雅地处理判空等问题.现在也有很多类库在使用Optional封装返回值,比如Sprin ...

  2. Linux系统的发展历史和学习前景介绍

    2020年了,我想来跟大家聊聊Linux运维这一行业,从几个方面说下行业的现状.如何学好Linux和如何成为专业运维人员以及云服务对于Linux运维的影响. 一.linux行业状况 我们都知道从199 ...

  3. sparkRDD:第3节 RDD常用的算子操作

    4.      RDD编程API 4.1 RDD的算子分类 Transformation(转换):根据数据集创建一个新的数据集,计算后返回一个新RDD:例如:一个rdd进行map操作后生了一个新的rd ...

  4. 第4节 Scala中的actor介绍:1、actor概念介绍;2、actor执行顺序和发送消息的方式

    10.    Scala Actor并发编程 10.1.   课程目标 10.1.1.    目标一:熟悉Scala Actor并发编程 10.1.2.    目标二:为学习Akka做准备 注:Sca ...

  5. linux 基本命令学习总结

    1. linux的目录结构  (linux核心:一切皆文件) 目录结构解释 (主要的有 /etc  /home  /mnt  /opt  /usr   /tmp) 相对路径和绝对路径的区别 绝对路径  ...

  6. 爬虫(十五):Scrapy框架(二) Selector、Spider、Downloader Middleware

    1. Scrapy框架 1.1 Selector的用法 我们之前介绍了利用Beautiful Soup.正则表达式来提取网页数据,这确实非常方便.而Scrapy还提供了自己的数据提取方法,即Selec ...

  7. 在idea中设置指向源代码(scala)

    1.到官网下载scala源代码 点击如下链接下载源码:http://www.scala-lang.org/download/all.html 选择需要的版本点击进行下载,我选择的是2.11.8版本,如 ...

  8. ORACLE 删除重复的数据

    内容转自:https://www.cnblogs.com/zfox2017/p/7676237.html         查询及删除重复记录的SQL语句   1.查找表中多余的重复记录,重复记录是根据 ...

  9. MariaDB——备份与恢复

    备份和恢复 为什么要备份?   灾难恢复:硬件故障.软件故障.自然灾害.黑客攻击.误操作   测试   要注意的点:   备份需要多少时间   能够容忍多少的数据丢失   恢复数据需要在多长时间完成  ...

  10. android EditText中inputType的属性列表

    android 1.5以后添加了软件虚拟键盘的功能,所以在输入提示中将会有对应的软键盘模式 android中inputType属性在EditText输入值时启动的虚拟键盘的风格有着重要的作用.这也大大 ...