目录

  • 场景介绍
  • selection介绍
  • selection API
  • range 介绍
  • range API
  • 实现编辑区插入表情图片
  • 参考资料

场景介绍

在写web版聊天器时,遇到一个需求:

聊天时用户可以在编辑区加入表情图片,并且表情图片要插入在光标位置。// *web版聊天器地址:
http://81.68.151.74/ (开发中,欢迎讨论)

就像这样:

刚开始没考虑这块内容,所以用的<textarea>标签,

而在该标签内,是无法显示出插入表情图片的。

这就需要用到HTML中的contenteditable="true"属性,来创建一个自定义编辑区。

就像这样:

<div class="box" contenteditable="true"></div>

然后对该自定义编辑区进行加工。

此过程中,需要根据用户点击的位置(即光标显示的位置)来显示插入的图片,

这就需要用到光标的控制。


selection介绍

光标控制的API分为两块内容:selection选区和range片段。

根据官方介绍,selection 对象所对应的是用户所选择的 ranges(区域),俗称拖蓝

默认情况下,该函数只针对一个区域。

因为一般情况下,用拖动鼠标只能同时选中一块区域。

在使用js脚本的情况下,range可能会出现多个。

因此,后者可以认为是前者的一部分,选区包含若干片段,一般是一个。

本篇就梳理下两者的常用API,理清操作流程。


selection API

selection 对象表示用户选择的文本范围或插入符号的当前位置。

它代表页面中的文本选区,可能横跨多个元素。文本选区由用户拖拽鼠标经过文字而产生。

获取selection

var selection = window.getSelection();

下面依次介绍selection的相关属性和方法。


首先需要注意的是,以下内容提到的起点终点,并不是固定的。

这和鼠标从左往右选择还是从右往左选择有关系。

先从哪里开始,哪里就是起点。


<!--属性-->
1. selection.anchorNode
只读属性,用于返回选区开始位置所属的节点。2. selection.anchorOffset
只读属性,返回选区的锚节点( Selection.anchorNode)起点偏移量的数字。
返回值从零开始计数,如果选区从锚节点(Selection.anchorNode)的第一个字符开始,
返回值为 0。3. selection.focusNode
只读属性,返回所选内容的结束位置部分所属的节点。4. selection.focusOffset
返回选区终点(鼠标松开瞬间所记录的那个点)在焦点(Selection.focusNode)中的偏移量。
返回值从零开始计数,如果选区(Selection)在焦点(Selection.focusNode)的第一个字符前结束,
返回值为 0。5. selection.isCollapsed
返回一个布尔值,用于描述选区的起始点和终止点是否位于一个位置,即是否框选了。6. selection.rangeCount
只读属性。用于返回选区 (selection) 中 range 对象数量
一般rangeCount总是为1,因为拖动鼠标只能选择一个区域。7. selection.type
只读属性,用于描述当前选区的选择类型。
结果有三个值:
- `None` 当前没有选择。
- `Caret`: 选区已折叠(即 光标在字符之间,并未处于选中状态)。
- `Range`: 选择的是一个范围。即框选了内容。<!--方法-->
1. selection.addRange(range)
向选区(Selection)中添加一个区域(Range)2. selection.collapse(node, offset)
设置光标在选区内的位置。
node是光标所落在的目标节点
offset是落在节点的偏移量3. selection.collapseToEnd()
取消当前选区,并把光标定位在原选区的最末尾处。无参数4. selection.collapseToStart()
取消当前选区,并把光标定位在原选区的最开始处。无参数5. selection.containsNode(node,isPartlyContained)
node是要被判断的节点
isPartlyContained表示node节点是否被包含
isPartlyContained为true时,就是要判断选区是否包含node,(此时包含一部分或者全部,都算被包含)
isPartlyContained为false时,就是要判断选区是否包含node的全部6. selection.deleteFromDocument()
删除选区7. selection.extend(node, offset)
移动选中区的焦点到指定的点。选中区的锚点不会移动。选中区将从锚点开始到新的焦点,不管方向。
node是表示焦点会被移动到node节点内
offset 可选参数,默认为0.表示偏移量8. var range = selection.getRangeAt(index)
返回一个包含当前选区内容的区域对象
index表示区域编号,从0开始。index永远要小于rangeCount
rangeCount一般为1,所以index一般是09. selection.modify(alter, direction, granularity)
通过简单的文本命令来改变当前选区或光标位置。
alter 改变类型。传入 "move" 来移动光标位置,或者 "extend" 来扩展当前选区。
direction 调整选区的方向。forward backward left right
granularity 调整的距离颗粒度。一次是移动一个字的举例,还是一个字符的距离等。10. selection.removeAllRanges()
从当前 selection 对象中移除所有的 range 对象,
取消所有的选择只 留下anchorNode 和focusNode属性并将其设置为 null
无参数。11. selection.removeRange(range)
将一个区域从选区中移除。
range 表示要被移除的区域12. selection.selectAllChildren(parentNode)
所有 parentNode 元素的子元素会被设为选中区域,
并取消之前的选中区域,parentNode 本身除外。13. selection.setBaseAndExtent(anchorNode,anchorOffset,focusNode,focusOffset)
用来选中并设置在两个特定的 DOM 节点中文本选中的范围,
并且选中的任何内容都位于两个节点之间。
anchorNode 锚节点 - 选中内容的开始节点
anchorOffset 选中范围内起点位置在锚节点下第几个子节点的位置。
例如,如果是值为 0 的话,整个节点都是被选中的。
如果值为 1 的话,那么至少整个节点至少有一个子节点被选中。以此类推。
focusNode 焦点节点 - 选中内容的结尾节点
focusOffset 选中范围内结束位置在焦点节点下第几个子节点的位置。
例如,如果是值为 0 的话,整个节点都是被选中的。
如果值为 1 的话,那么至少整个节点至少有一个子节点被选中。以此类推

range 介绍

range表示一部分文档片段。

可以通过多种方法创建或获取:

// 第一种 从selection中获取
var range = selection.getRangeAt();// 第二种 通过Range创建
var range = new Range()// 第三种 create创建
var range = document.createRange()// 注意:
// 如果是创建的range,在使用他的大多数方法之前需要去设置他的临界点。
// 也就是要确定光标

range API

<!--属性-->
1. range.collapsed
返回一个布尔值。表示是否起始点和结束点是同一个位置
如果返回true,就表示起始点和结束点重合。
如果返回false,就表示起始点和结束点没重合。2. range.endContainer
它会返回Range对象结束的Node3. range.endOffset
返回代表 Range 结束位置在 Range.endContainer 中的偏移值的数字4. range.startContainer
返回 Range 开始的节点5. range.startOffset
用于返回一个表示 Range 在 startContainer 中的起始位置的数字<!--方法-->
1. range.cloneContents()
克隆一个区域的所有节点。
节点的id属性也会被克隆,这点需要注意。
另外,节点的监听事件不会被克隆2. range.cloneRange()
克隆一个和原有range无关的range对象3. range.collapse(toStart)
用于折叠边界点。也就是 是否让光标重合
toStart为true,表示让光标重合,并在range的起始点出现
toStart为false,表示让光标重合,并在range的结束点出现4. range.insertNode(newNode);
在Range的起始位置插入节点
newNode是要插入的节点5. range.intersectsNode(node)
用来判断给定的节点node是否与range相交,返回一个布尔值6. range.selectNode(node);
将 Range 设置为包含整个 node 及其内容。
Range 的起始和结束节点的父节点与 node 的父节点相同。7. range.selectNodeContents(referenceNode);
用于设置 Range,使其包含一个 Node 的内容8. range.setEnd(endNode, endOffset);
设置光标的结束点9. range.setEndAfter(referenceNode)
设置光标的结束点在node节点之后10. range.setEndBefore(referenceNode)
设置光标的结束点在node节点之前11. range.setStart(startNode, startOffset);
设置 Range的开始位置12. range.setStartAfter(referenceNode)
设置range的开始位置在node节点之后13. range.setStartBefore(referenceNode)
设置range的开始位置在node节点之前14. range.surroundContents(newParent);
将 Range 对象的内容移动到一个新的节点,并将新节点放到这个范围的起始处。

实现编辑区插入表情图片

index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>* {padding: 0;margin: 0;}.wrap {position: relative;width: 500px;height: 200px;overflow: hidden;overflow-y: auto;margin-left: 100px;background-color: #f2a5a5;margin-top: 20px;box-sizing: border-box;padding: 10px;}.wrap .box {width: 100%;border: none;line-height: 28px;}.show {width: 500px;height: 200px;margin-left: 100px;border: none;border: 1px solid #000;line-height: 28px;margin-top: 20px;}.box[contenteditable] {outline: none;}.box img, .show img {width: 28px;vertical-align: middle;}.btn, .show_btn {width: 60px;height: 30px;text-align: center;line-height: 30px;margin-left: 300px;margin-top: 20px;}</style>
</head>
<body><div class="wrap"><div class="box" contenteditable="true"></div></div><button class="btn">插入表情</button><div class="show"></div><button class="show_btn">显示内容</button><script src="./index.js"></script>
</body>
</html>

index.js

var wrap = document.querySelector('.wrap'); // 编辑框的外层
var box = document.querySelector('.box'); // 编辑框
var sendEmoji = document.querySelector('.btn'); // 发送按钮
var img = document.createElement('img'); // 要插入的表情img
img.src = '   https://www.kktv5.com/images/facepic/good.gif';
var currentRange; // 记录光标位置// 点击编辑框时,记录下光标位置
box.onclick = function () {var selection = window.getSelection();currentRange = selection.getRangeAt(0);
}
// 上下左右键 enter 输入的内容等键也会改变光标位置,也要记录下来
box.onkeyup = function () {var selection = window.getSelection();currentRange = selection.getRangeAt(0);
}// 插入表情
sendEmoji.onclick = function () {box.focus();// 创建一个新的选区var selection = window.getSelection();// 如果光标位置原来就存在,就用原来的。// 原来不存在,就重新创建一个var range = currentRange ?? selection.getRangeAt(0);// 插入表情图标range.insertNode(img.cloneNode());// 插入后,光标显示在表情图片后面range.collapse();// 移除其他的区域selection.removeAllRanges();// 把带有表情图片的区域插入到选区内selection.addRange(range);
}var show = document.querySelector('.show');
var show_btn = document.querySelector('.show_btn');show_btn.onclick = function () {// 把编辑区的内容展示到另一个地方// 这里是用来模拟的接收人看到的结果show.innerHTML = box.innerHTML;
}// 用一层wrap包裹住编辑区,目的是解决光标无法自动捕捉表情图片的问题
// 这里点击wrap,让光标也能识别表情图片
wrap.onclick = function () {var selection = window.getSelection();if (currentRange) {selection.removeAllRanges();selection.addRange(currentRange);} else {selection.selectAllChildren(box);selection.collapseToEnd();}
}

参考资料

  • section
https://developer.mozilla.org/zh-CN/docs/Web/API/Selection
  • range
https://developer.mozilla.org/zh-CN/docs/Web/API/Range

【selection】 学习光标API并实现编辑区插入表情图片的功能相关推荐

  1. 关于可编辑div插入表情图标

    在最近的开发中因为项目需要一个可编辑插入表情的编辑框,起初也是头大煎熬,总是被我熬出来了. 在可编辑框中为难我们的是光标定位和在光标的位置插入指定的内容和表情 下面开始正事 节点内容是这样的我用的是v ...

  2. visual studio如何给编辑区添加背景图片,修改字体大小主题

    我们使用编辑器编写代码时总喜欢把编辑器搞得很个性化,今天给大家讲解一下visual studio如何变的跟个性化(我的visual studio 编辑器版本是1.18.1),至于怎么查看版本,帮助里面 ...

  3. eclipse2019-03设置代码编辑区背景为图片

    一.我的主题设置如下所示 二.找到如下所示或类似的文件夹 三.在该文件夹里的images文件夹里添加图片 四.在CSS目录下的e4-dark_win.css文件中添加如下代码 .MPart Style ...

  4. 给Eclipse代码编辑区设置背景图片

    1:找到Eclipse的安装路径,进入plugins目录 2:进入带ui主题的文件夹 3:主要关注的是css何images文件目录 在css文件增加以下样式 .MPart StyledText {

  5. Android音视频开发基础(六):学习MediaCodec API,完成视频H.264的解码

    前言 在Android音视频开发中,网上知识点过于零碎,自学起来难度非常大,不过音视频大牛Jhuster提出了<Android 音视频从入门到提高 - 任务列表>.本文是Android音视 ...

  6. html获取文本框光标位置,html 在编辑框中如何定位光标和获取光标最后位置

    在HTML里面,光标是一个对象,光标对象是只有当你选中某个元素的时候才会出现的. 当我们去点击一个输入框的时候,实际上它会产生一个选中对象-selection(就是我们可以看到的文字变成蓝色的那个区域 ...

  7. Linux学习----文件创建、修改和编辑

    Linux学习----文件创建.修改和编辑 Linux基础指令学习 1.1创建文件和修改时间戳(touch) touch用于创建空文件与修改时间戳.如果文件不存在,则会创建出一个空内容的文本文件:如果 ...

  8. 第六课 大数据技术之Fink1.13的实战学习-Table Api和SQL

    第六课 大数据技术之Fink1.13的实战学习-Table Api和SQL 文章目录 第六课 大数据技术之Fink1.13的实战学习-Table Api和SQL 第一节 Fink SQL快速上手 1. ...

  9. 设置eclipse中的编辑区的背景颜色、注释文字的颜色、修改注释内作者名和时间

    1.编辑区的背景颜色修改按以下步骤: window-->preferences-->general-->Editors-->Text Editors   然后在appearan ...

最新文章

  1. Codeforces Round #686 (Div. 3) F. Array Partition(二分+线段树)
  2. 谈谈Ext JS的组件——布局的使用方法续二
  3. 浙江省计算机二级(C语言)通过经验+资料
  4. oracle数据库11gr2,Oracle 11g R2 X64数据库安装
  5. mysql的number类型对应的db2_【转】oracle数据库NUMBER数据类型
  6. java 多线程数据分发_多线程分发处理List集合数据
  7. 【NOIP2015】【Luogu2669】金币(模拟)
  8. python界面颜色设置_pycharm修改界面主题颜色的方法
  9. 方维出现 Fatal error: Class 'Session' not found
  10. 计算机考试外贸出货单,史上最全订单跟进英文模板
  11. 强连通分量的一种类 Tarjan 算法以及Tarjan算法推导初探
  12. 计算机专业英语词汇缩写CIA,CIA英文词汇缩写
  13. 阿里云主要产品及功能介绍,阿里云产品分为6大分类:云计算基础/安全/大数据/人工智能/企业应用/物联网
  14. 科研论文画图技巧分享!超级实用!
  15. Java作业(2020 12 05)
  16. 【Revit二次开发】在轴网相交处创建柱子
  17. 70.用Dnsmasq构建DNS服务器
  18. CC3200学习笔记02-芯片简介
  19. jsonpath 判断是否包含_JsonNode findPath方法详解 JsonNode判断是否为空详解
  20. 【题解】P1971 [NOI2011] 兔兔与蛋蛋游戏

热门文章

  1. Android-APK瘦身实践
  2. 树莓派Pico直流步进电机接口技术及电机运动控制MicroPython+pioasm编程方法
  3. 【苹果群发】苹果推iMessage接口筛选应用程序利用密钥箱生成CSR文书
  4. Android webview 清除历史访问记录
  5. 沈航计算机考研上岸,22考研:上岸学姐亲身经历偷偷告诉你,一战成硕究竟到努力到什么程度?...
  6. 使用office2007打开excel报错:不是有效的win32文件
  7. 在react引入外部js把当前日期转换成阴历(农历)
  8. BGP Dampening Cyrus
  9. 判断当前音效是否播放完毕
  10. 同时查询京东多个快递物流,并分析中转延误