我有一个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)。
查克·诺里斯(Chuck Norris)可以接触MC Hammer。
测试
Caret位置= 4(时间|等待)
答案= <p id="a">
Caret位置= 16(可以触摸)。
答案= <p id="b">
Caret位置= 25(MC |锤)。
答案= <span id="c">
插入位置= 1000),没有答案(null
)。
答案 0 :(得分:1)
我已经对代码进行了很多评论,因此我认为它不需要更多的解释。只需使用父节点和索引作为参数调用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>