div可编辑,插入元素获取光标,getSelection()对象操作
本身在公司中碰到一个需求是点击按钮,在输入框中展示出按钮的文字。
查阅网上资料,发现在这方面给出的答案很少,要么就是和我的需求不同,我是用不了,要么就是写的太过复杂,很难优化,因为我看的也是云里雾里的。
所以通过对网上的其他资料研究,自己弄了个汇总的,帮助自己以后再碰到这种需求能够更快的解决。
这里用到的就是一个选区对象和区域对象。window.getSelection()获取选区对象。
Selection
对象表示用户选择的文本范围或插入符号的当前位置。它代表页面中的文本选区,可能横跨多个元素。文本选区由用户拖拽鼠标经过文字而产生。要获取用于检查或修改的 Selection 对象,请调用 window.getSelection()。包括了用户选中的文明范围或光标位置
还有一个区域对象range,window.getSelection().getRangeAt(0),编辑区中的光标位置操作更多还是在这个对象中实现,这是选区中我们选择的范围对象。
如果一上来对这两个对象没有了解的话,还是不建议接着往下看,因为我讲的不是很细,并没有对这两个对象做一个全面的介绍,我的更多细节会在代码中体现。
如果是对这两个对象有了解的话,可以接着往下看,因为我在代码中给了更多的解释,相较于其他的答案更加直观易懂,写的不算很复杂。
<body>
<!-- 实现一个功能:div变为可编辑,可向编辑框中插入元素和自定义文本,并且可以指定光标位置 -->
<div class="edit" contenteditable="true" oninput="editInput(event)"></div>
<!-- 该按钮指定向编辑框中插入元素 -->
<button class="nodeButton">向编辑框中插入元素</button>
<br>
<input type="text" placeholder="请输入文本">
<!-- 该按钮指定向编辑框中插入自定义文本 -->
<button class="textButton">向编辑框中输入文本</button>
<script>
const div = document.getElementsByClassName("edit")[0]
const body = document.getElementsByTagName("body")[0]
const nodeButton = document.getElementsByClassName("nodeButton")[0]
const textButton = document.getElementsByClassName("textButton")[0]
const input = document.getElementsByTagName("input")[0]
const creDOM = document.createElement("div")
creDOM.innerText = `${div.innerText.length}/100`
body.appendChild(creDOM)
// 这一个方法是记录输入字符数的,不需要关心
function editInput(e) {
creDOM.innerText = `${div.innerText.length}/100`
}
let range = ""
div.addEventListener("blur", () => {
// 这一步是保留住edit编辑框中的选区的范围对象。否则失焦后,getSelection()方法返回的选区对象已经不再是编辑框了,已经获取不到编辑框中的范围对象了。
range = window.getSelection().getRangeAt(0)
})
nodeButton.addEventListener("click", () => {
const span = document.createElement("span")
span.innerText = "我是一个span元素"
// 如果在页面刷新再点击编辑框之前就点击了按钮,此时range中并没有选区范围对象
if (range === "") {
let selection = window.getSelection()
selection.selectAllChildren(div) // selectAllChildren把指定元素的所有子元素设为选中区域,并取消之前的选中区域。不包括node节点本身。
/*
Selection.collapseToEnd() 方法的作用是取消当前选区,并把光标定位在原选区的最末尾处,如果此时光标所处的位置是可编辑的,且它获得了焦点,则光标会在原地闪烁。
以上selectAllChildren方法,将div中子节点全部选中,collapseToEnd方法将选中区域取消,并且将光标定位到原区的末尾。
*/
selection.collapseToEnd()
range = window.getSelection().getRangeAt(0) // 无论哪一步都需要保存当前编辑框的选区对象
}
let sel = window.getSelection()
range.insertNode(span) // insertNode方法,在range选区开头插入一个节点
/*
removeAllRanges方法:删除之前的所有选区。
这一步的意义:因为当我们点击其他区域时,选区对象已经改变,不再是编辑框中的选区对象,这时候我们接下来的操作都不会符合我们想象中的样子
*/
sel.removeAllRanges()
sel.addRange(range) // 这一步就是添加当前区域对象到选区对象中,所以选区对象会再次指向编辑框中的选区,不会出现别的错误操作。
sel.collapseToEnd()
})
// 只要知道如何在div编辑框中插入元素,那么文本节点也是同理的,对照上面的代码修改一点就可以完成需求
textButton.addEventListener("click", () => {
// 第一步获取input框中的内容,将其中的内容转为文本节点插入区域对象中
let inputText = input.value
const text = document.createTextNode(inputText)
// 如果在页面刷新再点击编辑框之前就点击了按钮,此时range中并没有选区范围对象
if (range === "") {
let selection = window.getSelection()
selection.selectAllChildren(div) // selectAllChildren把指定元素的所有子元素设为选中区域,并取消之前的选中区域。不包括node节点本身。
/*
Selection.collapseToEnd() 方法的作用是取消当前选区,并把光标定位在原选区的最末尾处,如果此时光标所处的位置是可编辑的,且它获得了焦点,则光标会在原地闪烁。
以上selectAllChildren方法,将div中子节点全部选中,collapseToEnd方法将选中区域取消,并且将光标定位到原区的末尾。
*/
selection.collapseToEnd()
range = window.getSelection().getRangeAt(0) // 无论哪一步都需要保存当前编辑框的选区对象
}
let sel = window.getSelection()
range.insertNode(text) // insertNode方法,在range选区开头插入一个节点
/*
removeAllRanges方法:删除之前的所有选区。
这一步的意义:因为当我们点击其他区域时,选区对象已经改变,不再是编辑框中的选区对象,这时候我们接下来的操作都不会符合我们想象中的样子
*/
sel.removeAllRanges()
/*
// 这一步就是添加当前区域对象到选区对象中,所以选区对象会再次指向编辑框中的选区,不会出现别的错误操作。
选区对象的指向如果为空,那么它将会由区域对象指定,区域对象只想哪里那么选区对象就指向哪里。这里选区对象为空了,添加编辑框中的区域对象,所以选区就指向了编辑框了。 (这里是我的猜想,我并没有找到直接证据证明是这样的,但是目前的现象好像确实如此)
*/
sel.addRange(range)
sel.collapseToEnd()
})
效果图: