laya.html.dom,javascript - How to set caret(cursor) position in contenteditable element (div)? - Sta...

I made this for my simple text editor.

Differences from other methods:

High performance

Works with all spaces

usage

// get current selection

const [start, end] = getSelectionOffset(container)

// change container html

container.innerHTML = newHtml

// restore selection

setSelectionOffset(container, start, end)

// use this instead innerText for get text with keep all spaces

const innerText = getInnerText(container)

const textBeforeCaret = innerText.substring(0, start)

const textAfterCaret = innerText.substring(start)

selection.ts

/** return true if node found */

function searchNode(

container: Node,

startNode: Node,

predicate: (node: Node) => boolean,

excludeSibling?: boolean,

): boolean {

if (predicate(startNode as Text)) {

return true

}

for (let i = 0, len = startNode.childNodes.length; i < len; i++) {

if (searchNode(startNode, startNode.childNodes[i], predicate, true)) {

return true

}

}

if (!excludeSibling) {

let parentNode = startNode

while (parentNode && parentNode !== container) {

let nextSibling = parentNode.nextSibling

while (nextSibling) {

if (searchNode(container, nextSibling, predicate, true)) {

return true

}

nextSibling = nextSibling.nextSibling

}

parentNode = parentNode.parentNode

}

}

return false

}

function createRange(container: Node, start: number, end: number): Range {

let startNode

searchNode(container, container, node => {

if (node.nodeType === Node.TEXT_NODE) {

const dataLength = (node as Text).data.length

if (start <= dataLength) {

startNode = node

return true

}

start -= dataLength

end -= dataLength

return false

}

})

let endNode

if (startNode) {

searchNode(container, startNode, node => {

if (node.nodeType === Node.TEXT_NODE) {

const dataLength = (node as Text).data.length

if (end <= dataLength) {

endNode = node

return true

}

end -= dataLength

return false

}

})

}

const range = document.createRange()

if (startNode) {

if (start < startNode.data.length) {

range.setStart(startNode, start)

} else {

range.setStartAfter(startNode)

}

} else {

if (start === 0) {

range.setStart(container, 0)

} else {

range.setStartAfter(container)

}

}

if (endNode) {

if (end < endNode.data.length) {

range.setEnd(endNode, end)

} else {

range.setEndAfter(endNode)

}

} else {

if (end === 0) {

range.setEnd(container, 0)

} else {

range.setEndAfter(container)

}

}

return range

}

export function setSelectionOffset(node: Node, start: number, end: number) {

const range = createRange(node, start, end)

const selection = window.getSelection()

selection.removeAllRanges()

selection.addRange(range)

}

function hasChild(container: Node, node: Node): boolean {

while (node) {

if (node === container) {

return true

}

node = node.parentNode

}

return false

}

function getAbsoluteOffset(container: Node, offset: number) {

if (container.nodeType === Node.TEXT_NODE) {

return offset

}

let absoluteOffset = 0

for (let i = 0, len = Math.min(container.childNodes.length, offset); i < len; i++) {

const childNode = container.childNodes[i]

searchNode(childNode, childNode, node => {

if (node.nodeType === Node.TEXT_NODE) {

absoluteOffset += (node as Text).data.length

}

return false

})

}

return absoluteOffset

}

export function getSelectionOffset(container: Node): [number, number] {

let start = 0

let end = 0

const selection = window.getSelection()

for (let i = 0, len = selection.rangeCount; i < len; i++) {

const range = selection.getRangeAt(i)

if (range.intersectsNode(container)) {

const startNode = range.startContainer

searchNode(container, container, node => {

if (startNode === node) {

start += getAbsoluteOffset(node, range.startOffset)

return true

}

const dataLength = node.nodeType === Node.TEXT_NODE

? (node as Text).data.length

: 0

start += dataLength

end += dataLength

return false

})

const endNode = range.endContainer

searchNode(container, startNode, node => {

if (endNode === node) {

end += getAbsoluteOffset(node, range.endOffset)

return true

}

const dataLength = node.nodeType === Node.TEXT_NODE

? (node as Text).data.length

: 0

end += dataLength

return false

})

break

}

}

return [start, end]

}

export function getInnerText(container: Node) {

const buffer = []

searchNode(container, container, node => {

if (node.nodeType === Node.TEXT_NODE) {

buffer.push((node as Text).data)

}

return false

})

return buffer.join('')

}

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值