2

I am having troubles figuring out how to get caret position in a DIV container that contains HTML tags.

I am using this JavaScript function to do that:

function  getCaretPosition()
{
    if (window.getSelection && window.getSelection().getRangeAt)
    {
            var range = window.getSelection().getRangeAt(0);
            var selectedObj = window.getSelection();
            var rangeCount = 0;
            var childNodes = selectedObj.anchorNode.parentNode.childNodes;
            for (var i = 0; i < childNodes.length; i++)
            {
                if (childNodes[i] == selectedObj.anchorNode)
                {
                        break;
                    }
                        if(childNodes[i].outerHTML)
                        {
                rangeCount += childNodes[i].outerHTML.length;
            }
            else if(childNodes[i].nodeType == 3)
            {
                            rangeCount += childNodes[i].textContent.length;                       
            }
        }
        return range.startOffset + rangeCount;
    }
    return -1;
}

However, it finds a caret position of the text in my DIV container, when I need to find the caret position including HTML tags. For example:

<DIV class="peCont" contenteditable="true">Text goes here along with <b>some <i>HTML</i> tags</b>.</DIV>;

(please note, that HTML tags are normal tags and are not displayed on the screen when the function is returning caret position)

If I click right between H and TML, the aforementioned function will find caret position without any problems. But I am getting the contents of DIV box in HTML format (including all tags), and if I want to insert something at that caret's position, I will be off by a few or many characters.

I went through many posts, but all I could find is either <TEXTAREA> caret postions, or functions similar to what I have posted. So far I still cannot find a solution to get a caret position in a text that has HTML formatting.

Can anyone help, please?

PS. Here's JQuery/Javascript code that I wrote for the link button:

$('#pageEditor').on('click', '.linkURL', function()
{
    var cursorPosition;
    cursorPosition = getCaretPosition();
    var contentID = $(this).parent().parent().attr('id');
    var userSelected = getSelectionHtml();
    var checkLink = userSelected.search('</a>');
    var anchorTag = 0;
    if(checkLink == -1)
    {
        var currentContents = $('#'+contentID+' .peCont').html();
        var indexOfSelection = currentContents.indexOf(userSelected);
        var getPossibleAnchor = currentContents.slice(indexOfSelection, indexOfSelection+userSelected.length+6);
        anchorTag = getPossibleAnchor.search('</a>');
    }

    if(checkLink > 0 || anchorTag > 0)
    {
        //alert(checkLink);
        document.execCommand('unlink', false, false);

    }
    else
    {
        $('#'+contentID+' .peCont').append('<div id="linkEntry"><label for="urlLink">Please enter URL for the link:<label><input type="text" id="urlLink" /></div>');
        $('#linkEntry').dialog({
             buttons: { 
                "Ok": function() 
                {
                    var attribute = $('#urlLink').val();
                    var newContentWithLink = '';
                    if(attribute != '')
                    {
                        if(userSelected != '')
                        {
                            var currentContent = $('#'+contentID+' .peCont').html();
                            var replacement = '<a href="'+attribute+'">'+userSelected+'</a>';
                            newContentWithLink = currentContent.replace(userSelected, replacement);
                        }
                        else
                        {
                            var currentTextContent = $('#'+contentID+' .peCont').html();
                            var userLink = '<a href="'+attribute+'">'+attribute+'</a>';
                            if(cursorPosition > 0)
                            {
                                var contentBegin = currentTextContent.slice(0,cursorPosition);
                                var contentEnd = currentTextContent.slice(cursorPosition,currentTextContent.length);
                                newContentWithLink = contentBegin+userLink+contentEnd;
                            }
                            else
                            {
                                newContentWithLink = attribute+currentTextContent;
                            }
                        }
                        $('#'+contentID+' .peCont').empty();
                        $('#'+contentID+' .peCont').html(newContentWithLink);
                    }
                    $(this).dialog("close"); 
                } },
             closeOnEscape:true,
             modal:true,
             resizable:false,
             show: { effect: 'drop', direction: "up" },
             hide: { effect: 'drop', direction: "down" },
             width:460,
             closeText:'hide',
             close: function()
             {
                $(this).remove();
             }
        });

        $('#linkEntry').on('keypress', function(urlEnter)
        {
            if(urlEnter.which == 13)
            {
                var attribute = $('#urlLink').val();
                var newContentWithLink = '';
                if(userSelected != '')
                {
                    var currentContent = $('#'+contentID+' .peCont').html();
                    var replacement = '<a href="'+attribute+'">'+userSelected+'</a>';
                    newContentWithLink = currentContent.replace(userSelected, replacement);
                }
                else
                {
                    var currentTextContent = $('#'+contentID+' .peCont').html();
                    var userLink = '<a href="'+attribute+'">'+attribute+'</a>';
                    if(cursorPosition > 0)
                    {
                        var contentBegin = currentTextContent.slice(0,cursorPosition);
                        var contentEnd = currentTextContent.slice(cursorPosition,currentTextContent.length);
                        newContentWithLink = contentBegin+userLink+contentEnd;
                    }
                    else
                    {
                        newContentWithLink = attribute+currentTextContent;
                    }
                }
                $('#'+contentID+' .peCont').empty();
                $('#'+contentID+' .peCont').html(newContentWithLink);
                $(this).dialog("close");
            }
        });
    }
});
Negmis
  • 21
  • 1
  • 3
  • What you're asking for is essentially meaningless: there are more than one different valid HTML string representations of the contents of your `
    `, and the original HTML content sent by the server (if that's what you're after) is inaccessible from the DOM (you'd have to do some nasty Ajax thing to get a new copy of the page and then parse the response). What exactly are you intending to do with this position once you've got it?
    – Tim Down Jun 13 '12 at 13:59
  • I am trying to insert some HTML formatting - let's say, an anchor. So I click between H and TML, and let's say hypothetically, its position is 10. Now a window pop up, user enters URL, hits Enter and I want to insert it back into my site- (but here's a problem - HTML is positioned at 80 - that's all those HTML tags). Now I am inserting an anchor at index 10, because that's what the script I am using does - which means results are unpredictable. I am trying to make custom CMS. – Negmis Jun 13 '12 at 21:06
  • In that case, all you need to do is save the selection range before popping up the dialog. Here is a similar question: http://stackoverflow.com/questions/6726579/insert-html-when-the-caret-was-in-a-content-editable-div. And here's how to handle a potential IE pitfall: http://stackoverflow.com/questions/5337752/inserting-text-into-an-editable-iframe-at-the-caret-position-ie – Tim Down Jun 14 '12 at 09:39
  • The first link does not work. I am using JQuery modal dialog box - where user enters enters their URL. Every time they hit submit, the link is inserted at the beginning of the text, not current caret position. I am going to alter my question and add the code for the link button that I have created above. – Negmis Jun 14 '12 at 11:08
  • You need to store the caret position before the editable `
    ` loses focus.
    – Tim Down Jun 14 '12 at 11:11
  • I did assign cursorPosition = saveSelection(); or do I have to do it before the user even clicks on the button? – Negmis Jun 14 '12 at 11:15
  • Yes, before the click event fires, at least. I think `mousedown` is early enough. – Tim Down Jun 14 '12 at 11:25
  • Obviously I am doing something wrong. It still inserts the link at the beginning of the `
    ` container. I am very new with javascript and JQuery, so I am not getting where the proper placement will occur. I know that JQuery is event-based - so I placed `var cursorPosition = saveSelection();` when I just click inside of the `
    `, not the link button and when I `alert(cursorPosition);` - the alert box is blank
    – Negmis Jun 14 '12 at 11:51

1 Answers1

-1

I created a simple fiddle for you that does what you want.

This should work in recent versions of Firefox, Chrome and Safari.

It's your homework to add Opera and IE support if you need.

zupa
  • 12,809
  • 5
  • 40
  • 39