我有一个带有文字内容的html页面。在选择任何文本并按突出显示按钮时,我可以更改所选文本的样式以突出显示相同的文本。为了实现这个功能,我写了以下方法。
sel = window.getSelection();
var range = sel.getRangeAt(0);
var span = document.createElement('span');
span.className = "highlight" + color;
range.surroundContents(span);
如果选择没有html标记的文本,但是当文本中间有任何html标记时,这是正常的,它会给出错误
无法执行' surroundContents' on' Range':Range已部分选择了非Text节点。
如何解决这个问题。是否可以为每个部分分别突出显示相同的内容(除以html标签)?
答案 0 :(得分:26)
document.getElementById('execute').addEventListener('click', function() {
var range = window.getSelection().getRangeAt(0),
span = document.createElement('span');
span.className = 'highlight';
span.appendChild(range.extractContents());
range.insertNode(span);
});
.highlight { background-color: yellow; }
<div id="test">
Select any part of <b>this text and</b> then click 'Run'.
</div>
<button id="execute">Run</button>
答案 1 :(得分:5)
我不是重新发明轮子,而是使用Rangy的突出显示功能。
我分配了RGraham created的小提琴并创建了一个显示其工作原理的new fiddle。这就是它的完成方式:
var applier = rangy.createClassApplier("highlight");
var highlighter = rangy.createHighlighter();
highlighter.addClassApplier(applier);
document.getElementById('execute').addEventListener('click', function() {
highlighter.removeAllHighlights();
highlighter.highlightSelection("highlight");
});
这样做会创建一个荧光笔,它会在完全位于选区内的元素上设置highlight
类,并根据需要为跨越选区的元素创建highlight
类。单击标识为execute
的按钮时,将删除旧的高亮显示并应用新的高光。
荧光笔功能是Rangy发布的一部分,被认为是&#34; alpha&#34;。 然而,我一直在使用Rangy的alpha版本几年了,但是我发现我的应用程序出现了问题,因为它非常罕见追溯到Rangy。有几次我发现Rangy有问题,Tim Down(其作者)非常敏感。
答案 2 :(得分:2)
试试这个:
newNode.appendChild(range.extractContents())
根据MDN:
克隆部分选定的节点以包含父标记 使文档片段有效所必需的。
但是,如果Range拆分非Text,则会抛出异常 节点只有一个边界点。也就是说,不像 上面的替代方案,如果有部分选定的节点,他们会 不能克隆,而是操作失败。
没有测试,但是......
答案 3 :(得分:2)
这个解决方案有点棘手,但我发现它就足够了
当您仔细查看我们通过调用
获得的选择对象时matplotlib
您将看到有4个属性:window.getSelection().getRangeAt(0)
,startContainer
,startOffset
,endContainer
。
现在您需要从endOffset
开始startContainer
,然后从那里开始放置必要的span节点。
如果现在它startOffset
是不同的节点,那么您需要开始遍历从endContainer
到startContainer
的节点
对于遍历,您需要检查可以从DOM对象获取的子节点和兄弟节点。首先浏览endContainer
,遍历它的所有子节点并检查子节点是否为内联元素,然后在其周围应用span标记,然后你需要为各种边角情况编写少量编码。
答案 4 :(得分:0)
我的解决方案突出显示了所有选定的节点。
function highlight() {
const sel = window.getSelection();
const range = sel.getRangeAt(0);
const {
commonAncestorContainer,
startContainer,
endContainer,
startOffset,
endOffset,
} = range;
const nodes = [];
console.group("range");
console.log("range", range);
console.log("commonAncestorContainer", commonAncestorContainer);
console.log("startContainer", startContainer);
console.log("endContainer", endContainer);
console.log("startOffset", startOffset);
console.log("endOffset", endOffset);
console.log("startContainer.parentNode", startContainer.parentNode);
console.groupEnd();
if (startContainer === endContainer) {
const span = document.createElement("span");
span.className = "highlight";
range.surroundContents(span);
return;
}
// get all posibles selected nodes
function getNodes(childList) {
console.group("***** getNode: ", childList);
childList.forEach((node) => {
console.log("node:", node, "nodoType", node.nodeType);
const nodeSel = sel.containsNode(node, true);
console.log("nodeSel", nodeSel);
// if is not selected
if (!nodeSel) return;
const tempStr = node.nodeValue;
console.log("nodeValue:", tempStr);
if (node.nodeType === 3 && tempStr.replace(/^\s+|\s+$/gm, "") !== "") {
console.log("nodo agregado");
nodes.push(node);
}
if (node.nodeType === 1) {
if (node.childNodes) getNodes(node.childNodes);
}
});
console.groupEnd();
}
getNodes(commonAncestorContainer.childNodes);
console.log(nodes);
nodes.forEach((node, index, listObj) => {
const { nodeValue } = node;
let text, prevText, nextText;
if (index === 0) {
prevText = nodeValue.substring(0, startOffset);
text = nodeValue.substring(startOffset);
} else if (index === listObj.length - 1) {
text = nodeValue.substring(0, endOffset);
nextText = nodeValue.substring(endOffset);
} else {
text = nodeValue;
}
const span = document.createElement("span");
span.className = "highlight";
span.append(document.createTextNode(text));
const { parentNode } = node;
parentNode.replaceChild(span, node);
if (prevText) {
const prevDOM = document.createTextNode(prevText);
parentNode.insertBefore(prevDOM, span);
}
if (nextText) {
const nextDOM = document.createTextNode(nextText);
parentNode.insertBefore(nextDOM, span.nextSibling);
}
});
sel.removeRange(range);
}
示例https://codesandbox.io/s/api-selection-multiple-with-nodes-gx2is?file=/index.html
答案 5 :(得分:-1)
解决方案真的很棘手。 我以某种方式找到了解决方法。查看我的fiddle
function highlight() {
var range = window.getSelection().getRangeAt(0),
parent = range.commonAncestorContainer,
start = range.startContainer,
end = range.endContainer;
var startDOM = (start.parentElement == parent) ? start.nextSibling : start.parentElement;
var currentDOM = startDOM.nextElementSibling;
var endDOM = (end.parentElement == parent) ? end : end.parentElement;
//Process Start Element
highlightText(startDOM, 'START', range.startOffset);
while (currentDOM != endDOM && currentDOM != null) {
highlightText(currentDOM);
currentDOM = currentDOM.nextElementSibling;
}
//Process End Element
highlightText(endDOM, 'END', range.endOffset);
}
function highlightText(elem, offsetType, idx) {
if (elem.nodeType == 3) {
var span = document.createElement('span');
span.setAttribute('class', 'highlight');
var origText = elem.textContent, text, prevText, nextText;
if (offsetType == 'START') {
text = origText.substring(idx);
prevText = origText.substring(0, idx);
} else if (offsetType == 'END') {
text = origText.substring(0, idx);
nextText = origText.substring(idx);
} else {
text = origText;
}
span.textContent = text;
var parent = elem.parentElement;
parent.replaceChild(span, elem);
if (prevText) {
var prevDOM = document.createTextNode(prevText);
parent.insertBefore(prevDOM, span);
}
if (nextText) {
var nextDOM = document.createTextNode(nextText);
parent.appendChild(nextDOM);
}
return;
}
var childCount = elem.childNodes.length;
for (var i = 0; i < childCount; i++) {
if (offsetType == 'START' && i == 0)
highlightText(elem.childNodes[i], 'START', idx);
else if (offsetType == 'END' && i == childCount - 1)
highlightText(elem.childNodes[i], 'END', idx);
else
highlightText(elem.childNodes[i]);
}
}
答案 6 :(得分:-2)
if (window.getSelection) {
var sel = window.getSelection();
if (!sel) {
return;
}
var range = sel.getRangeAt(0);
var start = range.startContainer;
var end = range.endContainer;
var commonAncestor = range.commonAncestorContainer;
var nodes = [];
var node;
for (node = start.parentNode; node; node = node.parentNode){
var tempStr=node.nodeValue;
if(node.nodeValue!=null && tempStr.replace(/^\s+|\s+$/gm,'')!='')
nodes.push(node);
if (node == commonAncestor)
break;
}
nodes.reverse();
for (node = start; node; node = getNextNode(node)){
var tempStr=node.nodeValue;
if(node.nodeValue!=null && tempStr.replace(/^\s+|\s+$/gm,'')!='')
nodes.push(node);
if (node == end)
break;
}
for(var i=0 ; i<nodes.length ; i++){
var sp1 = document.createElement("span");
sp1.setAttribute("class", "highlight"+color );
var sp1_content = document.createTextNode(nodes[i].nodeValue);
sp1.appendChild(sp1_content);
var parentNode = nodes[i].parentNode;
parentNode.replaceChild(sp1, nodes[i]);
}
}