views:

104

answers:

3

When some text on a document is highlighted, the default browser highlight is lost as soon as there is a click on the document.

I want to keep the browser highlight on all the time just like apture http://www.apture.com/. Highlight some text, it will popup with a "Learn More" bubble, click on the "Learn More" button, it still won't lose the default browser highlight focus.

How do I do that?

I basically want to get the position of the selected text without adding a span AND keeping the browser highlight when clicked on a button.

A: 

You probably need to look at selection range in JavaScript and then add a span with a class for a hook with CSS.

span.highlight {
   background: red;
}
alex
I am already using rangy http://code.google.com/p/rangy/ to wrap the selected text in a span. The problem is that I don't want to add a custom background-color to the span. I want it to just use default browser highlight styling.
Haris
@Haris That may not be an option.
alex
How can Apture manage to do that?
Haris
@Haris: I think Apture is explicitly setting the selection when you click on the "Learn more" button.
Tim Down
Tim, how can I do that? :)
Haris
@Haris: I've posted an answer.
Tim Down
+1  A: 

Here's a simple example of how to retain the selection when the user clicks a particular element. It stores the selection Range(s) or TextRange (IE <= 8) in the mousedown event of the element and reselects those ranges in the mouseup event.

<script type="text/javascript">
    var saveSelection, restoreSelection;
    if (window.getSelection) {
        // IE 9 and non-IE
        saveSelection = function() {
            var sel = window.getSelection(), ranges = [];
            if (sel.rangeCount) {
                for (var i = 0, len = sel.rangeCount; i < len; ++i) {
                    ranges.push(sel.getRangeAt(0));
                }
            }
            return ranges;
        };

        restoreSelection = function(savedSelection) {
            var sel = window.getSelection();
            sel.removeAllRanges();
            for (var i = 0, len = savedSelection.length; i < len; ++i) {
                sel.addRange(savedSelection[i]);
            }
        };
    } else if (document.selection && document.selection.createRange) {
        // IE <= 8
        saveSelection = function() {
            var sel = document.selection;
            return (sel.type != "None") ? sel.createRange() : null;
        };

        restoreSelection = function(savedSelection) {
            if (savedSelection) {
                savedSelection.select();
            }
        };
    }

    window.onload = function() {
        var specialDiv = document.getElementById("special");
        var savedSel = null;

        specialDiv.onmousedown = function() {
            savedSel = saveSelection();
        };

        specialDiv.onmouseup = function() {
            restoreSelection(savedSel);
        };
    };
</script>

<p>
    Select something up here and click on one of the areas below.
    <b>The first will lose the selection</b>, while the second will keep it.
</p>
<div style="border: solid blue 1px">Normal div</div>
<div id="special" style="border: solid blue 1px">Special div.
    Press me and keep the selection</div>
Tim Down
Downvoter: Please explain. This answer works and answers the question.
Tim Down
+2  A: 

I've used code from here and there's a working example at http://jsfiddle.net/jrdGW/

Select some text in the top paragraph and then click on the bottom paragraph. The selection will be restored after 1 second (to show that it stores it).

Tested in Chrome, FF and IE.

Code (in case jsfiddle is down):

var RNG = null;

function GSel() {
    var d = document;
    if (d.selection) {
        return d.selection.type == "Text" ? d.selection : null;
    }
    if (window.getSelection) {
        return window.getSelection();
    }
    return null;
}

function CRng() {
    var sel = GSel();
    if (sel) {
        if (sel.createRange) {
            return sel.createRange();
        }
        if (sel.rangeCount && sel.getRangeAt) {
            return sel.getRangeAt(0);
        }
    }
    return null;
}

function Sel(rng) {
    if (rng.select) {
        rng.select();
    }
    else {
        var s = GSel();
        if (s.removeAllRanges && s.addRange) {
            s.removeAllRanges();
            s.addRange(rng);
        }
    }
}


$(document).ready(function() {
    $('#learn').mousedown(function() {
        RNG = CRng();
        setTimeout(function() {
            if (RNG) {
                Sel(RNG);
            }
        }, 1000);
    });
});
Blair McMillan
This is essentially the same as my answer, which has now received a completely unjustified downvote. Is using jsfiddle now required for all answers?
Tim Down
I posted it because I couldn't get your answer to work in any browser (I didn't down vote).
Blair McMillan
Really? Works for me in all browsers. Even works if you copy just the code verbatim into an empty file without bothering to put `<html>` and `<body>` tags in.
Tim Down