javascript - 查找给定插入符号位置的节点(javascript)
问题描述
我有一个 HTML 文件,并且我有一个假定的插入符号位置。使用 JS,如果插入符号位于给定位置,我需要找到插入符号所在的 html 节点。
function findNodeForCaretPosition(caretPosition) {
var node = null; // type Node (https://developer.mozilla.org/en-US/docs/Web/API/Node)
// TODO find node
return node;
}
示例 HTML
<html>
<body>
<div>
<p id="a">Time waits for no man. Unless that man is Chuck Norris.</p>
</div>
<div>
<p id="b">Chuck Norris can touch <span id="c" style="color:blue">MC Hammer</span>.</p>
</div>
</body>
</html>
示例 HTML 纯文本
时间不等人。除非那个人是查克·诺里斯。
Chuck Norris 可以触摸 MC Hammer。
测试
插入符号位置 = 4(时间|等待)
答案 =<p id="a">
插入符号位置 = 16(可以|触摸)。
答案 =<p id="b">
插入符号位置 = 25(MC| 锤子)。
答案 =<span id="c">
插入符号位置 = 1000),没有答案 ( null
)。
解决方案
我已经对代码进行了相当多的评论,所以我认为它不需要更多的解释。只需caretPosition
使用父节点和索引作为参数调用。由于 JavaScript 中的字符串是 UTF-16,因此任何表情符号或非 ASCII 字符都应计为一个字符而不是多个字符。
值得注意的是,空格很重要。因此,从技术上讲,您问题中的 HTML 不起作用,因为它首先计算换行符和前导空格。为了简单起见,我在这里删除了它。
如果您有任何问题,请在评论中告诉我。
/**
* @param {Element} parent
* @param {number} index
*/
function caretPosition(parent, index) {
// The index is too large to fit in the element, return `null` per requirements.
// We also return `null` if the element isn't a text or element node,
// as there is no text to check against.
if (
(parent.nodeType === Node.ELEMENT_NODE && index > parent.innerText.length)
|| (parent.nodeType === Node.TEXT_NODE && index > parent.data.length)
|| ![Node.ELEMENT_NODE, Node.TEXT_NODE].includes(parent.nodeType)
){
return null;
}
// The length of all text combined to this point
// (zero is the beginning of `parent`).
let combinedLength = 0;
// Iterate over the children until we find
// the element where we cross the boundry.
for (const child of parent.childNodes) {
// Store this in case we need to recurse.
const previousLength = combinedLength;
// For the current child, add the length of its text content.
// As text and element nodes don't share a common property,
// we need to explicitly check for both. Other node types
// (such as comments) are irrelevant to the task at hand.
if (child.nodeType === Node.TEXT_NODE) {
combinedLength += child.data.length;
} else if (child.nodeType === Node.ELEMENT_NODE) {
combinedLength += child.innerText.length;
} else {
// We don't have a text or element node,
// so there's no text that we could care about.
// The recursive case will handle the fact that nothing changed,
// and will return `null`.
continue;
}
// Our cursor is inside or at the end of this node.
if (index <= combinedLength) {
// We are in a text node and have nothing to recurse on.
// Return the parent element of the text node,
// which is a DOM element.
if (child.nodeType === Node.TEXT_NODE) {
return child.parentElement;
}
// If we are in an element node, then we have no children.
// Without children, there is nothing to recurse on;
// we should return the element.
// If we are _not_ in an element node,
// this will be `false`, and we will enter the recursive case.
else if (child.childElementCount === 0) {
return child;
}
// We have children to iterate over, so do that.
// It is necessary to change the index to search for
// as we've got a new reference frame.
return caretPosition(child, index - previousLength);
}
}
}
// Make sure everything works!
const div = document.querySelectorAll('div');
console.assert(caretPosition(div[0], 4) === document.querySelector('#a'));
console.assert(caretPosition(div[1], 16) === document.querySelector('#b'));
console.assert(caretPosition(div[1], 25) === document.querySelector('#c'));
console.assert(caretPosition(div[0], 1000) === null);
console.assert(caretPosition(div[1], 1000) === null);
<div><p id='a'>Time waits for no man. Unless that man is Chuck Norris.</p></div>
<div><p id='b'>Chuck Norris can touch <span id='c' style='color:blue'>MC Hammer</span>.</p></div>
推荐阅读
- opengl - 在 OpenGL 中创建椭圆或椭圆光源?
- javascript - JavaScript:使用按钮更改 HTML 中的图像
- android - 使用意图将项目数据从 Firestore UI RecyclerView 发送到下一个活动
- javascript - 如何跳过前 2 个承诺休息来解决
- php - 对于表格中的每个帖子未正确提交
- c# - 如果从文件中读取字符串,则无法将 UTF8 转码为 ASCII
- php - 理解流明语法:router->get()
- c++ - c++ boost http程序,什么是“需要缓冲区”错误?
- reactjs - 如何在redux中为同一个查询存储多个结果
- angularjs - 在同一个控制器中重复使用模板两次,但显示不同的数据