[英]javascript doing multiple createrange() while encountering unexpected anchorOffset
My goal:我的目标:
Let users highlight different substring in a single long string.
让用户在单个长字符串中突出显示不同的子字符串。
However, once I've highlighted one substring with range.surroundContents(newNode)
(newNode is a span
with yellow background), the innerHTML
of the whole long string changed-- it started to contain the span
element;但是,一旦我用
range.surroundContents(newNode)
突出显示了一个子字符串(newNode 是一个黄色背景的span
),整个长字符串的innerHTML
发生了变化——它开始包含span
元素; consequently, if the user wants to highlight a substring after the previous highlighted substring in the same long string, the anchorOffset
will return the index starting after
the previous span.因此,如果用户想在同一长字符串中突出显示前一个突出显示的子字符串之后的子字符串,
anchorOffset
将返回从前一个跨度after
开始的索引。
For example, in this long string:例如,在这个长字符串中:
"Mr. and Mrs. Dursley, of number four, Privet Drive, were proud to say that they were perfectly normal, thank you very much."
“女贞路四号的德思礼夫妇很自豪地说他们完全正常,非常感谢你们。”
this long sentence is wrapped by a p
whose class name is noting
.这个长句子被一个
p
包裹着,它的类名是noting
。 If the range.surroundContents()
method the substring "Privet Drive", then, when I want to get the window.getSelection().anchorOffset
of the substring "thank", the answer wrongly is 53 while the correct answer should be 102 .如果
range.surroundContents()
方法是子字符串“Privet Drive”,那么,当我想获取子字符串“thank”的window.getSelection().anchorOffset
时,错误的答案是53而正确的答案应该是102 。 How should I do?我应该怎么做? Thank you!!
谢谢!!
PS I don't want to use substring method to find the position, thank you! PS我不想用substring方法找位置,谢谢!
$(".noting").mouseup(function(e){
$("#noteContent").val("");/*flushing*/
curSentNum = $(this).attr("id").split("-")[1];
$('#curSentNum').val(curSentNum);
highlightLangName = $(this).attr("id").split("-")[2];
$('#highlightLangName').val(highlightLangName);
//console.log(".noting $(this).html()"+$(this).html()+" "+$(this).attr("id"));//id, for example: p-2-French
if (window.getSelection) {
highlightedText = window.getSelection().toString();
curAnchorOffset = window.getSelection().anchorOffset;
$('#anchorAt').val(curAnchorOffset);
$('#highlightLen').val(highlightedText.length);
}
else if (document.selection && document.selection.type != "Control") {
highlightedText = document.selection.createRange().text;
}
});
And then I'll save the anchorAt
information to db;然后我将
anchorAt
信息保存到db; after the db operation, I'll immediately call this function using the previous variables remained:在 db 操作之后,我会立即使用之前保留的变量调用这个函数:
function highlightNoteJustSaved(){
var curI = noteCounter;
var anchorAt = parseInt($("#anchorAt").val());
var highlightLen = parseInt($("#highlightLen").val());
/*p to find, for example: p-2-French*/
var curP = document.getElementById('p-'+curSentNum.toString()+"-"+$("#highlightLangName").val());
var range = document.createRange();
root_node = curP;
range.setStart(root_node.childNodes[0], anchorAt);
range.setEnd(root_node.childNodes[0], anchorAt+highlightLen);
var newNode = document.createElement("span");
newNode.style.cssText="background-color:#ceff99";//yellow
newNode.className = alreadyNoteStr;
newNode.setAttribute('id','already-note-'+curI.toString());
range.surroundContents(newNode);
}
for HTML tree node structure, please take a look at the comment below( I didn't figure out how to copy-paste the code at this asking area).对于 HTML 树节点结构,请看下面的评论(我没有弄清楚如何在此询问区域复制粘贴代码)。
I replaced your method to highlight text with 2 methods.我用两种方法替换了您突出显示文本的方法。
highlightTextNodes
finds the word in the content of the node. highlightTextNodes
在节点的内容中查找单词。 Searching each child.寻找每一个孩子。 Also I implemented a highlight remover to show how it works.
我还实现了一个高光去除器来展示它是如何工作的。 I replaced the
span
with a mark
tag.我用
mark
标签替换了span
。
let alreadyNoteStr = 'already'; let noteCounter = 0; let elementId; $('p.noting').mouseup(function(e) { elementId = $(this).attr('id'); $('#noteContent').val(''); /*flushing*/ curSentNum = elementId.split('-')[1]; $('#curSentNum').val(curSentNum); highlightLangName = elementId.split('-')[2]; $('#highlightLangName').val(highlightLangName); //console.log(".noting $(this).html()"+$(this).html()+" "+$(this).attr("id"));//id, for example: p-2-French if (window.getSelection) { highlightedText = window.getSelection().toString(); curAnchorOffset = window.getSelection().anchorOffset; $("#noteContent").val(highlightedText); $('#anchorAt').val(curAnchorOffset); $('#highlightLen').val(highlightedText.length); highlight(elementId, highlightedText); } else if (document.selection && document.selection.type != "Control") { highlightedText = document.selection.createRange().text; } }); function highlightNoteJustSaved() { let curI = noteCounter; let anchorAt = parseInt($("#anchorAt").val()); let highlightLen = parseInt($("#highlightLen").val()); /*p to find, for example: p-2-French*/ let curP = document.getElementById('p-' + curSentNum.toString() + "-" + $("#highlightLangName").val()); let range = document.createRange(); rootNode = curP; let childNode = rootNode.childNodes[0]; range.setStart(rootNode.childNodes[0], anchorAt); range.setEnd(rootNode.childNodes[0], anchorAt + highlightLen); var newNode = document.createElement("span"); newNode.style.cssText = "background-color:#ceff99"; //yellow newNode.className = alreadyNoteStr; newNode.setAttribute('id', 'already-note-' + curI.toString()); range.surroundContents(newNode); } /* * Takes in an array of consecutive TextNodes and returns a document fragment with `word` highlighted */ function highlightTextNodes(nodes, word) { if (!nodes.length) { return; } let text = ''; // Concatenate the consecutive nodes to get the actual text for (var i = 0; i < nodes.length; i++) { text += nodes[i].textContent; } let fragment = document.createDocumentFragment(); while (true) { // Tweak this if you want to change the highlighting behavior var index = text.toLowerCase().indexOf(word.toLowerCase()); if (index === -1) { break; } // Split the text into [before, match, after] var before = text.slice(0, index); var match = text.slice(index, index + word.length); text = text.slice(index + word.length); // Create the <mark> let mark = document.createElement('mark'); mark.className = 'found'; mark.appendChild(document.createTextNode(match)); // Append it to the fragment fragment.appendChild(document.createTextNode(before)); fragment.appendChild(mark); } // If we have leftover text, just append it to the end if (text.length) { fragment.appendChild(document.createTextNode(text)); } // Replace the nodes with the fragment nodes[0].parentNode.insertBefore(fragment, nodes[0]); for (var i = 0; i < nodes.length; i++) { let node = nodes[nodes.length - i - 1]; node.parentNode.removeChild(node); } } /* * Highlights all instances of `word` in `$node` and its children */ function highlight(id, word) { let node = document.getElementById(id); let children = node.childNodes; let currentRun = []; for (var i = 0; i < children.length; i++) { let child = children[i]; if (child.nodeType === Node.TEXT_NODE) { // Keep track of consecutive text nodes currentRun.push(child); } else { // If we hit a regular element, highlight what we have and start over highlightTextNodes(currentRun, word); currentRun = []; // Ignore text inside of our <mark>s if (child.nodeType === Node.ELEMENT_NODE && child.className !== 'found') { highlight(child, word); } } } // Just in case we have only text nodes as children if (currentRun.length) { highlightTextNodes(currentRun, word); } } /* * Removes all highlighted <mark>s from the given node */ function unhighlight(id) { let node = document.getElementById(id); let marks = [].slice.call(node.querySelectorAll('mark.found')); for (var i = 0; i < marks.length; i++) { let mark = marks[i]; // Replace each <mark> with just a text node of its contents mark.parentNode.replaceChild(document.createTextNode(mark.childNodes[0].textContent), mark); } }
label { display: block; position: relative; padding-left: 100px; } button { margin-top: 20px; margin-bottom: 20px; padding: 10px; } label>span { position: absolute; left: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <button type="button" onclick="unhighlight(elementId);">Unhighlight</button> <div id="div-0" class="only-left-border"> <p class="lan-English noting" id="p-1-English">Mr. and Mrs. Dursley, of number four, Privet Drive, were proud to say that they were perfectly normal, thank you very much.</p> </div> <label><span>Content:</span><input type="text" id="noteContent"></input></label> <label><span>Numer:</span><input type="text" id="curSentNum"></input></label> <label><span>Language:</span><input type="text" id="highlightLangName"></input></label> <label><span>Anchor:</span><input type="text" id="anchorAt"></input></label> <label><span>Length:</span><input type="text" id="highlightLen"></input></label>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.