Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Insert several elements inside an editable div at cursor position

I have a div with the contenteditable attribute. The user needs to be able to type and insert several select menus where the cursor is. I've managed to get the cursor position and to insert the first select menu, but it only works on the first text node.

That's how I get the cursor position:

function getCaretCharacterOffsetWithin(element) {
    var caretOffset = 0;
    var doc = element.ownerDocument || element.document;
    var win = doc.defaultView || doc.parentWindow;
    var sel;
    if (typeof win.getSelection != "undefined") {
            sel = win.getSelection();
            if (sel.rangeCount > 0) {
                    var range = win.getSelection().getRangeAt(0);
                    var preCaretRange = range.cloneRange();
                    preCaretRange.selectNodeContents(element);
                    preCaretRange.setEnd(range.endContainer, range.endOffset);
                    caretOffset = preCaretRange.toString().length;
                }
            } else if ( (sel = doc.selection) && sel.type != "Control") {
              var textRange = sel.createRange();
              var preCaretTextRange = doc.body.createTextRange();
              preCaretTextRange.moveToElementText(element);
              preCaretTextRange.setEndPoint("EndToEnd", textRange);
              caretOffset = preCaretTextRange.text.length;
          }
          return caretOffset;
    }

Then I update it every time the user types or clicks.

function updatePos() {
    var el = document.getElementById("msg");   
    pos = getCaretCharacterOffsetWithin(el);
}
document.body.onkeyup = updatePos;
document.body.onmouseup = updatePos;

Then here's how I'm handling the button that adds the select. I'm not sure how to insert an element after a text node, so I insert a br tag and remove it later. There has to be a cleaner way, right?

$('#btn').click(function(){
        var selectList = document.createElement('select');
        var msg = $('#msg');
        $(msg).html(function(){
                var first = $(msg).html().substring(0, pos);
                var last =  $(msg).html().substring(pos);
                return first + '<br>' + last;
        });

        $(msg).contents().filter('br').after(selectList);

        $(msg).contents().filter('br').remove();

        $(msg).focus();
})

I guess the problem is that I'm using substring to split the text and be able to insert the select there, and as soon as there is another select tag, the substring is not able to go past the first text node. So maybe I'm supposed to redo the whole thing with a different approach, but I'm completely stuck.

Here's the jsfiddle: https://jsfiddle.net/8a63sosr/

Thanks!

like image 735
pincfloit Avatar asked Dec 17 '15 14:12

pincfloit


People also ask

How do I make a div element editable?

Answer: Use the HTML5 contenteditable Attribute You can set the HTML5 contenteditable attribute with the value true (i.e. contentEditable="true" ) to make an element editable in HTML, such as <div> or <p> element.

How to set the caret cursor position in a contenteditable element div)?

In order to set caret cursor position in content editable elements like div tag is carried over by JavaScript Range interface. The range is created using document. createRange() method.

How do I set the cursor position in input?

Use the setSelectionRange() method to set the current text selection position to the end of the input field. Call the focus() method on the input element. The focus method will move the cursor to the end of the input element's value.

What is the use of contenteditable in HTML?

The contenteditable attribute specifies whether the content of an element is editable or not. Note: When the contenteditable attribute is not set on an element, the element will inherit it from its parent.


1 Answers

The problem is that for HTML elements it takes, well, in terms of jQuery, text(), not html().

I guess there's a better solution with some range parameters, but here's something:

    //fix caret pos
    var temlOffset = pos;
    $('#msg').html().split(/(<[^>]+>)/g).forEach(function(el){
        if(temlOffset > 0){
            if(el.length && el[0] === '<'){
               pos += el.length;
            } else {
               temlOffset -= el.length;
            }
        }
    });

https://jsfiddle.net/Lemz17L8/

So, what it does is adding the html tag length to the pos value.

Best regards, Alexander

like image 102
Alexander Mikhalchenko Avatar answered Sep 30 '22 12:09

Alexander Mikhalchenko