JavaScript 获取输入时的光标位置及场景问题
来源:互联网 发布:mac app store中文版 编辑:程序博客网 时间:2024/04/29 14:37
前言
在输入编辑的业务场景中,可能会需要在光标当前的位置或附近显示提示选项。
比如社交评论中的@user
功能,要确保提示的用户列表总是出现在@
字符右下方,又或者是在自定义编辑器中 autocomplete 语法提示,都需要获取光标当前的位置作为参照点。
两种位置
对于 WEB 开发来讲,当我们提到某某元素的位置,通常是指这个元素相对于父级或文档的像素单位坐标。而对于输入框中光标,就有了额外的区分。
相对于内容
相对于内容,光标位于第几个字符之后,姑且称之为字符位置吧。
相对于UI
相对于UI,也就是跟普通页面元素一样的像素位置了。
插入或替换内容
在前言提到的场景中,也有在光标位置处插入内容的需求,比如对选取文字加粗text => <strong>text</strong>
等。
textarea
textarea
元素可以很容易获取到选择的一段文字的起止位置。如果当前没有选择文字,则两个位置值都为光标右侧字符的索引,从 0 开始。
// 开始位置textarea.selectionStart// 结束位置textarea.selectionEnd
对于加粗功能,有了起止位置,就能获取到选择的文字内容,然后对内容进行替换。
由于textarea
不能包含子元素,只有纯文本,所以基于textarea
实现加粗只能像用 Markdown 标记语法实现。
var selectedText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd)textarea.setRangeText('**'+ selectedText +'**')
textarea.setRangeText(text: String)
把选中的文字替换为其他内容。
contenteditable
也可能我们会使用contenteditable
属性把一个元素变为可编辑元素。而上面所用的属性和函数都是普通元素所没有的,所以要换一种姿势实现。
还是以加粗功能为例。
// 获取文档中选中区域var range = window.getSelection().getRangeAt(0)var strongNode = document.createElement('strong')// 选中区域文本strongNode.innerHTML = range.toString()// 删除选中区range.deleteContents()// 在光标处插入新节点range.insertNode(strongNode)
基于contenteditable
的可编辑元素,其中的内容均为子元素,文本为textNode
,加粗使用 HTML 元素,插入或替换是对元素的操作。
如果想使用操作内容的思路实现会比较麻烦,因为可以获取到的起止位置是基于子元素的。
<div contenteditable>hello<strong>你好</strong><big>w</big>orld</div>
假如选中的文字是你好wor
,调用相关 API 的输出如下。
// 当前在文档中选择的文本,document 和 window 都有这个函数// var selection = document.getSelection()var selection = window.getSelection()selection.anchorNode // 你好selection.anchorOffset // 0selection.focusNode // orldselection.focusOffset // 2// 或者使用 Rangevar range = selection.getRangeAt(0)range.startContainer // 你好range.startOffset // 0range.endContainer // orldrange.endOffset // 2
最终可以获取到起止元素以及选中区域在开始元素内容中的字符位置和在结束元素内容中的字符位置。
其中的起止元素均为textNode
类型,通过parentNode
获取到包裹元素。
range.startContainer.parentNode // <strong>你好</strong>range.endContainer.parentNode // <div contenteditable>...</div>
需要注意的是通过
Selection
和Rang
获取到起止位置是有方向之分的,从左向右选择和从右向左选择得到的值是正好相反的。
基于光标像素位置创建内容
这里就要开始用像素位置,同样分为两种实现来讲。
contenteditable
可编辑元素获取光标像素位置就像textarea
获取光标的字符位置一样简单。
var range = window.getSelection().getRangeAt(0)range.getBoundingClientRect() // { width, height, top, right, bottom, right }
这么具体的尺寸值,实现自动完成真是 So easy!
textarea
textarea
其中的内容都是纯文本,在 DOM 中不存在相关的对象,对于像素位置就得另作他想了。
基于行高和字体大小计算
// 1.获取光标结束位置var end = textarea.selectionEnd// 2.通过匹配光标之前文本中的换行符计算所在行var row = textarea.value.substring(0, end).match(/\r\n|\r|\n/).length// 3.计算 top,行高 * 行数 + 上填充 + 边框宽度var top = lineHeight * (row + 1) + paddingTop + borderWidth// 4.获取光标左侧的文本var leftText = textarea.value.split(/\r\n|\r|\n/)[row]// 5.影响一段文字所占宽度的因素太多,除字体大小、中英文、符号、字符间距等,还有字体、浏览器、系统等客观因素// var left = ...
这个方案的思路是没问题的,但是考虑所有问题的成本太高。
虽然可以创建测试元素去计算文本宽度,但这个方案本身是从严谨的角度出发的。与其混在一块,直接用取巧的办法更简单。
镜像元素
文本不支持定位?那我创建 DOM 好了。
原文地址:http://imys.net/20161125/cursor-offset-at-input.html// 光标位置var end = textarea.selectionEnd// 光标前的内容var beforeText = textarea.value.slice(0, end)// 光标后的内容var afterText = textarea.value.slice(end)// 对影响 UI 的特殊元素编码var escape = function(text) {return text.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g, '<br>')}// 创建镜像内容,复制样式var mirror = '<div class="'+ textarea.className +'">'+ escape(beforeText)+ '<span id="cursor">|</span>'+ escape(afterText)+ '</div>'// 添加到 textarea 同级,注意设置定位及 zIndex,使两个元素重合textarea.insertAdjacentHTML('afterend', mirror)// 通过镜像元素中的假光标占位元素获取像素位置var cursor = document.getElementById('cursor')cursor.getBoundingClientRect() // { width, height, top, right, bottom, right }
- JavaScript 获取输入时的光标位置及场景问题
- JavaScript获取光标的位置
- 通过 JavaScript 获取和设置光标在输入框中的位置(兼容 IE 及 Firefox)
- javascript获取光标位置
- JavaScript获取光标位置、定位光标位置
- JavaScript获取文本框光标的像素位置
- JavaScript获取文本框光标的像素位置
- JavaScript获取文本框光标的像素位置
- JavaScript获取文本框光标的像素位置
- JavaScript获取文本框光标的像素位置
- JavaScript获取文本框光标的像素位置
- JavaScript获取文本框光标的像素位置
- UITextField的光标定位在任意位置及银行卡号的输入问题
- 获取光标的位置
- 在javascript中如何获得textbox输入光标的位置
- 在javascript中如何获得textbox输入光标的位置
- 使用TextRange获取输入框中光标的位置
- 使用TextRange获取输入框中光标的位置
- 【MongoDB】批量导入CSV文件
- C++学习笔记(一) 位运算详解
- slam学习
- 父类子类初始化过程的讨论
- JVM垃圾回收算法
- JavaScript 获取输入时的光标位置及场景问题
- java中的regex
- 文章标题
- servlet的request详解
- WebService为什么不如RESTful API流行
- 常坐电脑前面,这些健康问题你注意到了么?
- FM算法详解
- fork()函数两次返回
- 【设计模式】原型模式