我想将选定的文本包装在具有跨度的 div 容器中,这可能吗?
用户将选择一个文本并单击一个按钮,在按钮单击事件中,我想用 span 元素包装所选文本。我可以使用选定的文本,window.getSelection()
但如何知道它在 DOM 结构中的确切位置?
我想将选定的文本包装在具有跨度的 div 容器中,这可能吗?
用户将选择一个文本并单击一个按钮,在按钮单击事件中,我想用 span 元素包装所选文本。我可以使用选定的文本,window.getSelection()
但如何知道它在 DOM 结构中的确切位置?
如果选择完全包含在单个文本节点中,则可以使用surroundContents()
从选择中获得的范围的方法来执行此操作。但是,这是非常脆弱的:如果选择不能在逻辑上被单个元素包围(通常,如果范围跨越节点边界,尽管这不是精确定义),则它不起作用。要在一般情况下执行此操作,您需要更复杂的方法。
此外,DOMRange
和window.getSelection()
IE < 9 不支持。对于这些浏览器,您将再次需要另一种方法。您可以使用诸如我自己的Rangy之类的库来规范浏览器行为,并且您可能会发现class appliermodule对这个问题很有用。
简单的surroundContents()
例子jsFiddle:http : //jsfiddle.net/VRcvn/
代码:
function surroundSelection(element) {
if (window.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(element);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
function wrapSelectedText() {
var selection= window.getSelection().getRangeAt(0);
var selectedText = selection.extractContents();
var span= document.createElement("span");
span.style.backgroundColor = "yellow";
span.appendChild(selectedText);
selection.insertNode(span);
}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam rhoncus gravida magna, quis interdum magna mattis quis. Fusce tempor sagittis varius. Nunc at augue at erat suscipit bibendum id nec enim. Sed eu odio quis turpis hendrerit sagittis id sit amet justo. Cras ac urna purus, non rutrum nunc. Aenean nec vulputate ante. Morbi scelerisque sagittis hendrerit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla tristique ligula fermentum tortor semper at consectetur erat aliquam. Sed gravida consectetur sollicitudin.
<input type="button" onclick="wrapSelectedText();" value="Highlight" />
这是对允许跨越元素边界的通用解决方案的尝试。可能不能很好地与图像等混合,也不能与非从左到右的文本混合,但对于简单的情况应该没问题。
首先,这是一个非常通用的函数,用于在 Range 对象定义的子树中从左到右遍历文本节点。这将为我们提供我们需要的所有文本片段:
function walkRange(range) {
let ranges = [];
let el = range.startContainer;
let elsToVisit = true;
while (elsToVisit) {
let startOffset = el == range.startContainer ? range.startOffset : 0;
let endOffset = el == range.endContainer ? range.endOffset : el.textContent.length;
let r = document.createRange();
r.setStart(el, startOffset);
r.setEnd(el, endOffset);
ranges.push(r);
/// Move to the next text container in the tree order
elsToVisit = false;
while (!elsToVisit && el != range.endContainer) {
let nextEl = getFirstTextNode(el.nextSibling);
if (nextEl) {
el = nextEl;
elsToVisit = true;
}
else {
if (el.nextSibling) el = el.nextSibling;
else if (el.parentNode) el = el.parentNode;
else break;
}
}
}
return ranges;
}
这利用此实用程序函数来获取子树中的第一个(最左侧)文本节点:
function getFirstTextNode(el) {
/// Degenerate cases: either el is null, or el is already a text node
if (!el) return null;
if (el.nodeType == 3) return el;
for (let child of el.childNodes) {
if (child.nodeType == 3) {
return child;
}
else {
let textNode = getFirstTextNode(child);
if (textNode !== null) return textNode;
}
}
return null;
}
一旦你调用了walkRanges
,你就可以使用surroundContents
它返回的内容来实际进行突出显示/标记。这是在一个函数中:
function highlight(range, className) {
range = range.getRangeAt ? range.getRangeAt(0) : range;
for (let r of walkRange(range)) {
let mark = document.createElement('mark');
mark.className = className;
r.surroundContents(mark);
}
}
并取消突出显示(假设您为突出显示使用了唯一的类名):
function unhighlight(sel) {
document.querySelectorAll(sel).forEach(el => el.replaceWith(...el.childNodes));
}
用法示例:
highlight(document.getSelection(), 'mySelectionClassName');
unhighlight('.mySelectionClassName')
有可能的。您需要使用范围 API 和 Range.surroundContents() 方法。它将包含内容的节点放置在指定范围的开头。见https://developer.mozilla.org/en/DOM/range.surroundContents
仅当您的选择仅包含文本而不包含 HTML 时, aroundContents 才有效。这是一个更灵活的跨浏览器解决方案。这将插入一个像这样的跨度:
<span id="new_selection_span"><!--MARK--></span>
跨度插入在选择之前,在最近的 HTML 开始标记之前。
var span = document.createElement("span");
span.id = "new_selection_span";
span.innerHTML = '<!--MARK-->';
if (window.getSelection) { //compliant browsers
//obtain the selection
sel = window.getSelection();
if (sel.rangeCount) {
//clone the Range object
var range = sel.getRangeAt(0).cloneRange();
//get the node at the start of the range
var node = range.startContainer;
//find the first parent that is a real HTML tag and not a text node
while (node.nodeType != 1) node = node.parentNode;
//place the marker before the node
node.parentNode.insertBefore(span, node);
//restore the selection
sel.removeAllRanges();
sel.addRange(range);
}
} else { //IE8 and lower
sel = document.selection.createRange();
//place the marker before the node
var node = sel.parentElement();
node.parentNode.insertBefore(span, node);
//restore the selection
sel.select();
}