我想突出显示(应用CSS)某个文本范围,由其开始和结束位置表示。这比看起来更困难,因为文本中可能还有其他标签需要忽略。
示例:
<div>abcd<em>efg</em>hij</div>
highlight(2, 6)
需要突出显示 "cdef
”而不删除标签。
我已经尝试使用 TextRange 对象,但没有成功。
提前致谢!
下面是一个函数,用于将选择设置为特定元素内的一对字符偏移量。这是幼稚的实现:它没有考虑任何可能不可见的文本(例如通过 CSS 或通过位于
<script>
或 <style>
元素内)并且可能存在浏览器差异(IE 与其他所有内容)带有换行符,并且不考虑折叠空白(例如 2 个或更多连续空格字符折叠为页面上的一个可见空间)。但是,它确实适用于所有主要浏览器中的示例。
对于另一部分,即突出显示,我建议使用
document.execCommand()
。您可以使用我下面的函数来设置选择,然后调用document.execCommand()
。您需要使文档在非 IE 浏览器中临时可编辑,命令才能发挥作用。请在此处查看我的答案以获取代码:getSelection&surroundContents across multiple labels
这是一个 jsFiddle 示例,展示了整个过程,适用于所有主要浏览器:http://jsfiddle.net/8mdX4/1211/
以及选择设置代码:
function getTextNodesIn(node) {
var textNodes = [];
if (node.nodeType == 3) {
textNodes.push(node);
} else {
var children = node.childNodes;
for (var i = 0, len = children.length; i < len; ++i) {
textNodes.push.apply(textNodes, getTextNodesIn(children[i]));
}
}
return textNodes;
}
function setSelectionRange(el, start, end) {
if (document.createRange && window.getSelection) {
var range = document.createRange();
range.selectNodeContents(el);
var textNodes = getTextNodesIn(el);
var foundStart = false;
var charCount = 0, endCharCount;
for (var i = 0, textNode; textNode = textNodes[i++]; ) {
endCharCount = charCount + textNode.length;
if (!foundStart && start >= charCount
&& (start < endCharCount ||
(start == endCharCount && i <= textNodes.length))) {
range.setStart(textNode, start - charCount);
foundStart = true;
}
if (foundStart && end <= endCharCount) {
range.setEnd(textNode, end - charCount);
break;
}
charCount = endCharCount;
}
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.selection && document.body.createTextRange) {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(true);
textRange.moveEnd("character", end);
textRange.moveStart("character", start);
textRange.select();
}
}
您可以看看这个强大的 JavaScript 实用程序如何工作,它支持选择多个 DOM 元素:
MASHA(Mark & Share 的缩写)允许您标记网页内容中有趣的部分并分享
http://mashajs.com/index_eng.html
它也在 GitHub 上https://github.com/SmartTeleMax/MaSha
甚至可以在 Mobile Safari 和 IE 上使用!
我知道这个问题实际上并不相关,但这正是我真正在寻找的。
Selection
Range
方法进行操作,像这样
document.getSelection().getRangeAt(0).surroundContents(YOUR_WRAPPER_NODE) // Adds wrapper
document.getSelection().getRangeAt(0).insertNode(NEW_NODE) // Inserts a new node
Range
方法。
我正在为此苦苦挣扎,而且我的搜索请求不正确,所以我决定将其发布在这里,以防会有像我这样的人。
再次抱歉回答无关紧要的问题。
以下解决方案不适用于 IE,您需要为此应用 TextRange 对象等。由于这使用选择来执行此操作,因此在正常情况下不应破坏 HTML,例如:
<div>abcd<span>efg</span>hij</div>
与
highlight(3,6);
输出:
<div>abc<em>d<span>ef</span></em><span>g</span>hij</div>
注意它如何将范围之外的第一个字符包装到
em
中,然后将 span
内的其余字符包装到新的字符中。就好像它只是在字符 3 处打开并在字符 6 处结束一样,它会给出无效的标记,例如:
<div>abc<em>d<span>ef</em>g</span>hij</div>
代码:
var r = document.createRange();
var s = window.getSelection()
r.selectNode($('div')[0]);
s.removeAllRanges();
s.addRange(r);
// not quite sure why firefox has problems with this
if ($.browser.webkit) {
s.modify("move", "backward", "documentboundary");
}
function highlight(start,end){
for(var st=0;st<start;st++){
s.modify("move", "forward", "character");
}
for(var st=0;st<(end-start);st++){
s.modify("extend", "forward", "character");
}
}
highlight(2,6);
var ra = s.getRangeAt(0);
var newNode = document.createElement("em");
newNode.appendChild(ra.extractContents());
ra.insertNode(newNode);
示例:http://jsfiddle.net/niklasvh/4NDb9/
编辑 看起来至少我的 FF4 有一些问题
s.modify("move", "backward", "documentboundary");
但同时,似乎没有它也能工作,所以我只是将其更改为
if ($.browser.webkit) {
s.modify("move", "backward", "documentboundary");
}
编辑 正如 Tim 指出的,修改仅从 FF4 开始可用,因此我采用了不同的方法来获取选择,不需要修改方法,希望使其与浏览器更加兼容(IE 仍然需要自己的解决方案)。
代码:
var r = document.createRange();
var s = window.getSelection()
var pos = 0;
function dig(el){
$(el).contents().each(function(i,e){
if (e.nodeType==1){
// not a textnode
dig(e);
}else{
if (pos<start){
if (pos+e.length>=start){
range.setStart(e, start-pos);
}
}
if (pos<end){
if (pos+e.length>=end){
range.setEnd(e, end-pos);
}
}
pos = pos+e.length;
}
});
}
var start,end, range;
function highlight(element,st,en){
range = document.createRange();
start = st;
end = en;
dig(element);
s.addRange(range);
}
highlight($('div'),3,6);
var ra = s.getRangeAt(0);
var newNode = document.createElement("em");
newNode.appendChild(ra.extractContents());
ra.insertNode(newNode);
基于 jQuery.highlight 插件的想法。
private highlightRange(selector: JQuery, start: number, end: number): void {
let cur = 0;
let replacements: { node: Text; pos: number; len: number }[] = [];
let dig = function (node: Node): void {
if (node.nodeType === 3) {
let nodeLen = (node as Text).data.length;
let next = cur + nodeLen;
if (next > start && cur < end) {
let pos = cur >= start ? cur : start;
let len = (next < end ? next : end) - pos;
if (len > 0) {
if (!(pos === cur && len === nodeLen && node.parentNode &&
node.parentNode.childNodes && node.parentNode.childNodes.length === 1 &&
(node.parentNode as Element).tagName === 'SPAN' && (node.parentNode as Element).className === 'highlight1')) {
replacements.push({
node: node as Text,
pos: pos - cur,
len: len,
});
}
}
}
cur = next;
}
else if (node.nodeType === 1) {
let childNodes = node.childNodes;
if (childNodes && childNodes.length) {
for (let i = 0; i < childNodes.length; i++) {
dig(childNodes[i]);
if (cur >= end) {
break;
}
}
}
}
};
selector.each(function (index, element): void {
dig(element);
});
for (let i = 0; i < replacements.length; i++) {
let replacement = replacements[i];
let highlight = document.createElement('span');
highlight.className = 'highlight1';
let wordNode = replacement.node.splitText(replacement.pos);
wordNode.splitText(replacement.len);
let wordClone = wordNode.cloneNode(true);
highlight.appendChild(wordClone);
wordNode.parentNode.replaceChild(highlight, wordNode);
}
}