1

I have a div (#recommendTextArea) which is editable, in which that I try to modify the innerHTML of this div when a user clicks on a list (this is called .display_box), the function looks like this. Basically it appends a span to the innerHTML of the div and then it hides the friendList, upon hiding it also tries to restoreTheSelection and before appending the extra span I called saveSelection.

$(".display_box").live("click",function() 
    {
        selRange = saveSelection();
        console.log(selRange);
        var username = $(this).attr('title');
        var old = $("#recommendTextArea").html();
        var content = old.replace(word, " "); //replacing @abc to (" ") space
        var E ="<span contenteditable='false'>"+ username + "</span> ";
        content = [content.slice(0, start), E, content.slice(start)].join('');
        $("#recommendTextArea").html(content);
        $("#friendsList").hide(function(){
            restoreSelection(selRange);
        });
    });

I have the following function to restore and save selection:

function saveSelection() {
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            return sel.getRangeAt(0);
        }
    } else if (document.selection && document.selection.createRange) {
        return document.selection.createRange();
    }
    return null;
}

function restoreSelection(range) {
    if (range) {
        if (window.getSelection) {
            sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
        } else if (document.selection && range.select) {
            range.select();
        }
    }
}

However this doesn't work as expected, the cursor is no where to be seen when I click on an item. What am I doing wrong here?

adit
  • 32,574
  • 72
  • 229
  • 373
  • You should wrap those function in the if statement which I had shown you previously. this is not complete code. Check the code again [here](http://stackoverflow.com/a/13950376/1577396). – Mr_Green May 17 '13 at 08:04
  • @Mr_Green I am using a different save and restore function than what you provided – adit May 17 '13 at 08:07

1 Answers1

2

You have a few issues:

1) Timing: the "click" event is way too late to grab selection (ALWAYS debug this, it's super easy to see the editable DIV has long lost focus and selection by that time). Use "mousedown" instead.

2) You can't store selection range like this - changing the selection context (in your case the innerHTML of the commonAncestorContainer) will wipe that range (for some reason even cloned range objects get wiped for me). If you manage to get a copy (via jQuery.extend for example) it will become invalid because the text node inside is not guaranteed to remain the same. My best guess is to go with storing start/end offset and if needed the related nodes as required by the range. Restore the range properties after the HTML is modified.

3) As with 1) focus is crucial to maintain selection, so that click on the list.. make sure you prevent the default before exiting the handler so focus and you new selection will remain in the DIV.

Can't figure out the exact use case from your code but this is my test sample and you can adjust from here as needed: http://jsfiddle.net/damyanpetev/KWDf6/

Damyan Petev
  • 1,585
  • 7
  • 10
  • I am trying to append the E to the cursor, not to the end – adit May 17 '13 at 12:17
  • I figured but you have way too many missing pieces in the snippet above, so I simplified it. I pointed out what you need to do to get it working, how you manipulate the inner HTML is up to you :) – Damyan Petev May 17 '13 at 12:23
  • can you explain what this does: selRange.setStart($("#recommendTextArea")[0].childNodes[0], startOffset); – adit May 17 '13 at 12:30
  • $("#recommendTextArea")[0].childNodes[0] is the DIV text (it is the original selection-related node) that I restore. The start and end are for that node and it didn't work when I tried with the div itself. – Damyan Petev May 17 '13 at 12:32
  • can you modify your snippet so that it appends E to the cursor? I am still a bit confused with this range stuff – adit May 17 '13 at 12:34
  • Yup, just use the `endOffset` for the slice as in: http://jsfiddle.net/damyanpetev/KWDf6/25/ – Damyan Petev May 17 '13 at 12:47
  • Okay this seemed to work! However one last issue I have is that after the mouseevent button the cursor doesn't blink any more. I'd have to click on the div again to bring it back.. any way I can set the focus? – adit May 17 '13 at 13:03
  • Notice this is how selection behaves - when you select even before you click there's no blinking caret. As a matter of fact moving the caret(cursor if you will) and selection are the same thing. So you can either have the selection or a caret. – Damyan Petev May 17 '13 at 13:30
  • Actually before the selection I do have a blinking cursor.. because the case is that someone is inputting Hello @Adi , and then the list shows up and I select the person's name from the autocomplete. Then logically after that I should get my blinking cursor back – adit May 17 '13 at 13:58
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/30135/discussion-between-damyan-petev-and-adit) – Damyan Petev May 17 '13 at 14:21
  • Thumb up for this "You can't store selection range like this - changing the selection context...". I had problem because I was trying to restore modified range, so I had to save range after modification to restore it. – Kersh May 14 '15 at 20:37