window.getSelection() offset with HTML tags?
Asked Answered
D

2

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?

Dulia answered 3/3, 2013 at 1:29 Comment(6)
Maybe this answer will helpRocray
That has the same problem/doesn't deal with my issue. But that you for the link.Dulia
What are you trying to accomplish?Cadence
@Cadence 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).Dulia
I agree Rangy is bloated, but the highlighter module does just what you want. Is it just file size that bothers you?Ivatts
No, it's the fact that in order to unhighlight, you need to select the text then unhighlight it.if you simply remove the highlight span element, it screws up the program because all bounds are saved in an array.Dulia
N
9

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://mcmap.net/q/1021846/-this-function-highlight-selected-text-how-can-i-delete-the-span-made-by-javascript-clicking-the-highlighted-text (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://mcmap.net/q/82496/-javascript-highlight-selected-range-button (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://mcmap.net/q/152117/-javascript-user-selection-highlighting (live example)

Other helpful solutions:

Nabors answered 5/3, 2013 at 4:35 Comment(13)
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?Dulia
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.Dulia
How about this: jsfiddle.net/FLwxj/53? Used this solution: https://mcmap.net/q/1021847/-how-to-combine-tags-as-long-as-they-have-the-same-classNabors
Nope. If you highlight some text, then highlight some text later on in the page, it merges the two together. It should only merge when they intersect.Dulia
@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.Nabors
@Charlie, here's another attempt: jsfiddle.net/FLwxj/61. Utilized .replace and some (hopefully, correct) regex.Nabors
Works perfectly. One more thing: Is there any way to assign a custom ID, say a number, to each highlight, that increments by one? I know I can do this, but I can't figure out how to get it to work when highlights overlap, meaning the highlight id's would have to merge... any ideas?Dulia
If the ID count doesn't have to retain its order, this works: jsfiddle.net/FLwxj/68.Nabors
It does have to retain it's order. The text that is selected will be appended below the p element, and will have the same id as the highlighted text. Then, if the highlighted text is merged with another section, the text below will merge as well. Maybe there's a better way to do this?Dulia
@Charlie: Sorry, I've been pretty busy lately. I think this is still possible. Hopefully, I can get to it this week.Nabors
@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.Nabors
I have a huge problem. This code works beautifully, but I need to change the markup of .text. Instead of using pre spacing, I need to wrap each line in a p tag. When I do this, however, the code breaks. Do you think you could modify this to work when the content is like this: <div class="text"><p>Line one</p><p>line two</p></div>? I would be willing to start another bounty, if you'd like. I can also do this in a new question.Dulia
I asked the new question here: #15733849Dulia
I
0
const range = selection.getRangeAt(0);
const newTag = document.createElement(tag);

range.surroundContents(newTag);
Irrelevant answered 15/10, 2023 at 22:50 Comment(1)
Your answer could be improved by adding more information on what the code does and how it helps the OP.Ahmednagar

© 2022 - 2024 — McMap. All rights reserved.