jquery Setting cursor position in contenteditable div
Asked Answered
C

6

36

The old version of the question is below, after researching more, I decided to rephrase the question. The problem as before is, I need to focus a contenteditable div without highlighting the text, doing straight up focus highlights the text in Chrome.

I realize that people solved this problems in textareas by resetting the caret position in the textarea. How can I do that with a contenteditable element? All the plugins I've tried only works with textareas. Thanks.

Old Phrasing of the question:

I have a contenteditable element that I want to focus, but only insofar as to place the cursor at the front of the element, rather selecting everything.

elem.trigger('focus'); with jquery selects all the text in the entire element in chrome. Firefox behaves correctly, setting the caret at the front of the text. How can I get Chrome to behave the way I want, or is focus perhaps not what I'm looking for.

Thanks all.

Concave answered 20/5, 2010 at 4:51 Comment(3)
This previous question may help with cursor positioning.Jeuz
Are you sure those work on contenteditable. I was not able to get those to work.Concave
@Mark: i have updated the code to latest jquery and modified the demo to work with rich content.Carboy
C
20

demo: http://so.lucafilosofi.com/jquery-setting-cursor-position-in-contenteditable-div/

      <div id="editable" contentEditable="true">
            <h2>Lorem</h2> <p>ipsum dolor <i>sit</i> 
               amet, consectetur <strong>adipiscing</strong> elit.</p> Aenean.
        </div>

    <script type="text/javascript">
        $(function () {
            $('[contentEditable="true"]').on('click', function (e) {
                if (!$(this).hasClass('editing')) {
                    var html = $(this).html();
                    if (html.length) {
                        var range = rangy.createRange();
                        $(this).toggleClass('editing').html('<span class="content-editable-wrapper">' + html + '</span>');
                        var $last = $(this).find('.content-editable-wrapper');
                        range.setStartAfter($last[0]);
                        range.collapse(false);
                        rangy.getSelection().setSingleRange(range);
                    }
                }
            }).on('blur', function () {
                $(this).toggleClass('editing').find('.content-editable-wrapper').children().last().unwrap();
            });
        });
    </script>
Carboy answered 27/5, 2010 at 10:8 Comment(4)
That's an interesting hack, great idea actually. Thank you. But the contenteditable is a wysiwyg editor, so I can't just substitute a textfield.Concave
An interesting idea, and valuable for some I'm sure, but this does not answer the question of how to set the carat position on a contenteditable div. Adding a "fake" input box is not really a solution; if I wanted to use an input, I would have used an input.Spake
does not answer the question. better answer at #1182200Election
i have updated the code to the latest jQuery version and modified the demo to work as expected also with rich content. without adding extra input or textarea.Carboy
C
30

Maybe I'm misreading the question, but wouldn't the following do (assuming an editable <div> with id "editable")? The timer is there because in Chrome, the native browser behaviour that selects the whole element seems to trigger after the focus event, thereby overriding the effect of the selection code unless postponed until after the focus event:

var div = document.getElementById("editable");

div.onfocus = function() {
    window.setTimeout(function() {
        var sel, range;
        if (window.getSelection && document.createRange) {
            range = document.createRange();
            range.selectNodeContents(div);
            range.collapse(true);
            sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
        } else if (document.body.createTextRange) {
            range = document.body.createTextRange();
            range.moveToElementText(div);
            range.collapse(true);
            range.select();
        }
    }, 1);
};

div.focus();
Cabochon answered 1/6, 2010 at 9:27 Comment(2)
this is great, but how do I put the focus at the end of the div instead of the front? jsfiddle.net/bq6jQ/1 #22600194Ilbert
@Squirrl: Change both instances of range.collapse(true); to range.collapse(false);.Cabochon
C
20

demo: http://so.lucafilosofi.com/jquery-setting-cursor-position-in-contenteditable-div/

      <div id="editable" contentEditable="true">
            <h2>Lorem</h2> <p>ipsum dolor <i>sit</i> 
               amet, consectetur <strong>adipiscing</strong> elit.</p> Aenean.
        </div>

    <script type="text/javascript">
        $(function () {
            $('[contentEditable="true"]').on('click', function (e) {
                if (!$(this).hasClass('editing')) {
                    var html = $(this).html();
                    if (html.length) {
                        var range = rangy.createRange();
                        $(this).toggleClass('editing').html('<span class="content-editable-wrapper">' + html + '</span>');
                        var $last = $(this).find('.content-editable-wrapper');
                        range.setStartAfter($last[0]);
                        range.collapse(false);
                        rangy.getSelection().setSingleRange(range);
                    }
                }
            }).on('blur', function () {
                $(this).toggleClass('editing').find('.content-editable-wrapper').children().last().unwrap();
            });
        });
    </script>
Carboy answered 27/5, 2010 at 10:8 Comment(4)
That's an interesting hack, great idea actually. Thank you. But the contenteditable is a wysiwyg editor, so I can't just substitute a textfield.Concave
An interesting idea, and valuable for some I'm sure, but this does not answer the question of how to set the carat position on a contenteditable div. Adding a "fake" input box is not really a solution; if I wanted to use an input, I would have used an input.Spake
does not answer the question. better answer at #1182200Election
i have updated the code to the latest jQuery version and modified the demo to work as expected also with rich content. without adding extra input or textarea.Carboy
K
3

Yes it happens because you have used

elem.trigger('focus'); 

try to use class or to identify the element on which you want to fire a trigger event.

Kennie answered 20/5, 2010 at 5:9 Comment(0)
O
1

I managed to solve that problem after a bit of poking around the DOM.

elm.focus();
window.getSelection().setPosition(0);

It probably only works on WebKit browsers, but since it is the only source of the problem, I added a conditional (using jQuery)

if(!$.browser.webkit) {
    elm.focus();
} else {
    elm.focus();
    window.getSelection().setPosition(0);
}

Hope this solves your problem.

Orthopteran answered 9/6, 2010 at 17:26 Comment(1)
hmm, not sure why, that doesn't work for me. Tim Down's answer does though.Concave
P
0

Set pointer position in pre or div tag:

function setCursor(pos) {
    var el = document.getElementById("asd");
    var range = document.createRange();
    var sel = window.getSelection();
    range.setStart(el.childNodes[0], pos);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
    el.focus();
}

$('button').click(function () {
    setCursor(5);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="asd" contenteditable="true" >
asd
two
</div>
<button>Set caret position</button>

Source: https://social.msdn.microsoft.com/Forums/en-US/f91341cb-48b3-424b-9504-f2f569f4860f/getset-caretcursor-position-in-a-contenteditable-div?forum=winappswithhtml5

Physicality answered 29/1, 2018 at 4:13 Comment(0)
R
0

for example for inserting image in cursor position:

var insertImgTag = document.createElement("div");
insertImgTag.id = "insertImgTag";
if (document.getSelection().anchorNode != null) {
    document.getSelection().getRangeAt(0).insertNode(insertImgTag);}

then select created element:

$("#insertImgTag")
            .parent()
            .html(
                "<img src=/images/" + $("#name").val() + " width='" + imgWidth + "%' alt='" + imgAlt + "'>"
            );
Respiratory answered 4/11, 2022 at 4:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.