简体   繁体   中英

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!

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM