8

If I have the following HTML:

<div class="content">
Vivamus <span>luctus</span> urna sed urna ultricies ac tempor dui sagittis.
</div>

And I run an event on mouseup that sees the ranges of the selected text:

$(".content").on("mouseup", function () {
    var start = window.getSelection().baseOffset;
    var end = window.getSelection().focusOffset;
    if (start < end) {
        var start = window.getSelection().baseOffset;
        var end = window.getSelection().focusOffset;
    } else {
        var start = window.getSelection().focusOffset;
        var end = window.getSelection().baseOffset;
    }
    console.log(window.getSelection());
    console.log(start + ", " + end);
});

And I select the word Vivamus from the content, it will log 1, 8, as that is the range of the selection.

If, however, I select the word urna, it will log 15, 20, but won't take into account the <span> elements of the HTML.

Is there anyway for focusOffset and baseOffset to also count for HTML tags, instead of just the text?

6
  • Maybe this answer will help Commented Mar 3, 2013 at 1:37
  • That has the same problem/doesn't deal with my issue. But that you for the link. Commented Mar 3, 2013 at 5:13
  • What are you trying to accomplish? Commented Mar 5, 2013 at 2:54
  • @PetersenDidIt In the long run? I need to be able to select text, then have <span> elements surround the text, so that I can give the effect that they're highlighted. When you selected text that is already selected and text that's not, I want them to merge into one highlight. I don't like the plugins that are out there for this, as they're way too bloated, especially Rangy (I tried to use it). Commented Mar 5, 2013 at 3:00
  • I agree Rangy is bloated, but the highlighter module does just what you want. Is it just file size that bothers you? Commented Mar 6, 2013 at 17:41

2 Answers 2

9
+50

Update

Live Example: http://jsfiddle.net/FLwxj/61/

Using a clearSelection() function and a replace approach, I was able to achieve the desired result.

var txt = $('#Text').html();
$('#Text').html(
    txt.replace(/<\/span>(?:\s)*<span class="highlight">/g, '')
);
clearSelection();

Sources:


Below you'll find some working solutions to your problem. I placed them in order of code efficiency.

Working Solutions

  • https://stackoverflow.com/a/8697302/1085891 (live example)

    window.highlight = function() {
        var selection = window.getSelection().getRangeAt(0);
        var selectedText = selection.extractContents();
        var span = document.createElement("span");
        span.style.backgroundColor = "yellow";
        span.appendChild(selectedText);
        span.onclick = function (ev) {
        this.parentNode.insertBefore(
            document.createTextNode(this.innerHTML), 
            this
        );
        this.parentNode.removeChild(this);
        }
        selection.insertNode(span);
    }
    
  • https://stackoverflow.com/a/1623974/1085891 (live example)

    $(".content").on("mouseup", function () {
       makeEditableAndHighlight('yellow'); 
    });
    
    function makeEditableAndHighlight(colour) {
        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);
        }
    }
    
  • https://stackoverflow.com/a/12823606/1085891 (live example)

Other helpful solutions:

Sign up to request clarification or add additional context in comments.

13 Comments

I like the first option, as it's simpler. The only thing is that it doesn't provide for collision of the span elements. What I mean by this is that they don't merge if they overlap. Do you think that would be hard to implement?
For instance, I can merge the two if there is a span within a larger span: jsfiddle.net/charlescarver/FLwxj/52, but I can't figure out how to get it to work with merging text if the beginning or end matches text within the selection it's overlapping.
How about this: jsfiddle.net/FLwxj/53? Used this solution: stackoverflow.com/a/12004367/1085891
@charlie, you're right; I posted that too quickly. It looks like the textnodes make this a little tricky. I'm still playing with the code and will post something soon-ish.
@Charlie. How does this look: jsfiddle.net/FLwxj/82 I don't claim it's the most efficient way of accomplishing the task. I think it's close to accomplishing what you're after.
|
0
const range = selection.getRangeAt(0);
const newTag = document.createElement(tag);

range.surroundContents(newTag);

1 Comment

Your answer could be improved by adding more information on what the code does and how it helps the OP.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.