使用Javascript更改所选文本的CSS

12

我正在尝试制作一个JavaScript书签,作为一个高亮器,在书签被按下时将网页上所选文本的背景更改为黄色。

我正在使用以下代码来获取所选文本,并且它工作正常,返回正确的字符串。

function getSelText() {
    var SelText = '';
    if (window.getSelection) {
        SelText = window.getSelection();
    } else if (document.getSelection) {
        SelText = document.getSelection();
    } else if (document.selection) {
        SelText = document.selection.createRange().text;
    }
    return SelText;
}

然而,当我创建了一个类似的函数来使用jQuery更改所选文本的CSS时,它并没有起作用:

function highlightSelText() {
    var SelText;
    if (window.getSelection) {
        SelText = window.getSelection();
    } else if (document.getSelection) {
        SelText = document.getSelection();
    } else if (document.selection) {
        SelText = document.selection.createRange().text;
    }
    $(SelText).css({'background-color' : 'yellow', 'font-weight' : 'bolder'});
}

有任何想法吗?


1
可能是重复问题:https://dev59.com/q3E85IYBdhLWcg3w2HRa,https://dev59.com/JnE85IYBdhLWcg3w2HVa和https://dev59.com/-knSa4cB1Zd3GeqPSvhH。 - Tim Down
6个回答

19

用最简单的方法实现这个功能可以使用execCommand(),这个命令可以在所有的现代浏览器中改变背景颜色。

以下代码可以适用于包括跨越多个元素的任何选择。在非IE浏览器上,它开启designMode,应用背景颜色,然后再次关闭designMode

更新:

在IE 9中已经修复。

function makeEditableAndHighlight(colour) {
    var range, sel = window.getSelection();
    if (sel.rangeCount && sel.getRangeAt) {
        range = sel.getRangeAt(0);
    }
    document.designMode = "on";
    if (range) {
        sel.removeAllRanges();
        sel.addRange(range);
    }
    // Use HiliteColor since some browsers apply BackColor to the whole block
    if (!document.execCommand("HiliteColor", false, colour)) {
        document.execCommand("BackColor", false, colour);
    }
    document.designMode = "off";
}

function highlight(colour) {
    var range, sel;
    if (window.getSelection) {
        // IE9 and non-IE
        try {
            if (!document.execCommand("BackColor", false, colour)) {
                makeEditableAndHighlight(colour);
            }
        } catch (ex) {
            makeEditableAndHighlight(colour)
        }
    } else if (document.selection && document.selection.createRange) {
        // IE <= 8 case
        range = document.selection.createRange();
        range.execCommand("BackColor", false, colour);
    }
}

9
这是一个简单的示例,展示了它如何工作。正如Zack所指出的那样,您需要注意选择跨越多个元素的情况。这并不是要直接使用的,只是帮助您启发灵感的东西。在Chrome中测试通过。
var selection = window.getSelection();
var text = selection.toString();
var parent = $(selection.focusNode.parentElement);
var oldHtml = parent.html();
var newHtml = oldHtml.replace(text, "<span class='highlight'>"+text+"</span>");
parent.html( newHtml );

1
看起来不错,谢谢。在这个例子中,父元素通常是什么?如果父元素包含多个字符串实例会怎样? - Chris Armstrong
在我的情况下,父元素是包含<p>标签的。你提到了字符串的重复实例,特别是如果它们突出显示了一个常见的单词,这一点很好。我还不确定如何解决这个问题,但如果我想到了什么,我会发布的。 - Greg W

2
为了让高亮永久存在,我认为你需要将所选择的内容包裹在一个新的DOM元素中(使用span标签即可),然后给该元素添加样式属性。我不知道jQuery是否可以帮你完成这个任务。请注意,选择可能跨越元素边界,因此在一般情况下,你需要注入大量新元素。

2
请看我在http://www.jsfiddle.net/hbwEE/3/上制作的小例子。
它并没有考虑跨越多个元素的选择。(尽管IE会这样做,但会有一点html混乱..

1
在Firefox中,您可以使用::-moz-selection伪类。
在Webkit中,您可以使用::selection伪类。

谢谢,但我想永久更改CSS,这样即使我取消选择它,它也会保持高亮状态。还需要跨浏览器工作。 - Chris Armstrong

0

我喜欢Tim的答案,它干净而快速。但它也关闭了与高亮交互的可能性。

直接在文本周围插入内联元素是一个糟糕的选择,因为它们会破坏文本流并在复杂情况下搞乱事情。

所以我建议使用不太正规的方法

  1. 计算所选文本每行的绝对布局(无论它们在哪里),
  2. 然后在文档主体的末尾插入带颜色、半透明的内联块元素。

这个Chrome扩展程序就是一个如何实现这一点的例子。

它使用这个库的API来获取每个选定行的绝对布局。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接