Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle caret position in content editable div

I have this content editable div in HTML

<div contenteditable="true" id="TextOnlyPage"></div>

And heres my jquery code

var rxp = new RegExp("(([0-9]+\.?[0-9]+)|([0-9]+))", "gm");
$('#TextOnlyPage').keyup(function(e){
    if(e.keyCode == 13){
        e.preventDefault();
        $('#TextOnlyPage').children().each(function() {
            if ( $(this).is("div") ) {
                $(this).contents().unwrap();
            }});
        $('#TextOnlyPage').append("<br/>");
    }
        $('#TextOnlyPage').children().contents().unwrap();
        var $this = $(this);
        var content = $this.html();
        $this.html(content.replace(rxp, "<span class='highlight'>$1</span>"));});

Problem is the caret position, because when it apply span tags around numbers in string.replace method then cursor goes to the start of the content editable div. Also it can't goes to next line while I press enter key.

I know I can handle it through range and selection objects, but can't find a useful resource to understand how these objects works.

Kindly either provide me solution for this problem and it will be better if the solution in angularjs, or provide me a resource where I can understand range and selection objects with working examples.

like image 373
EMM Avatar asked Nov 09 '22 15:11

EMM


1 Answers

I faced the same issue as you did a while ago, to undertand how range and selection works I recommend you to take a look at MDN docs Selection and Range

One reliable way of detecting change in a contentEditable div is the event input. One issue I found when listenning for keyUp and preventing the default behavior for certain keyCode is that they are tons of them and it's very hard to cover all use cases.

What I prefer to do is to let the normal event being processed, save the caret position, get the text value and replace the parts that needs to be highlighted and then set the caret position back.

Here is a simple example

var contentEditable = document.querySelector('div[contenteditable]');
contentEditable.addEventListener('input', onInput);

var onInput = function(e) {

    // this function will be called after the current call-stack
    // is finished, at that time the user input will be inserted
    // in the content editable
    setTimeout(function() {

        // save the caret position

        var text = contentEditable.innerText;
        // perform all your replacement on `text`
        contentEditable.innerHTML = text;

        // set the caret position back
    }, 0);
}

I also wrote two handy method getCaretOffsetWithin and setCaretPositionWithin to help manipulate the caret when you want to perform some DOM manipulation and re-position the caret at its initial value after the processing.

You can check them on this gist contenteditable-utils.js

like image 77
Germain Avatar answered Nov 14 '22 22:11

Germain