How to move cursor to end of contenteditable entity How to move cursor to end of contenteditable entity javascript javascript

How to move cursor to end of contenteditable entity


Geowa4's solution will work for a textarea, but not for a contenteditable element.

This solution is for moving the caret to the end of a contenteditable element. It should work in all browsers which support contenteditable.

function setEndOfContenteditable(contentEditableElement){    var range,selection;    if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+    {        range = document.createRange();//Create a range (a range is a like the selection but invisible)        range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range        range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start        selection = window.getSelection();//get the selection object (allows you to change selection)        selection.removeAllRanges();//remove any selections already made        selection.addRange(range);//make the range you have just created the visible selection    }    else if(document.selection)//IE 8 and lower    {         range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)        range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range        range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start        range.select();//Select the range (make it the visible selection    }}

It can be used by code similar to:

elem = document.getElementById('txt1');//This is the element that you want to move the caret to the end ofsetEndOfContenteditable(elem);


If you don't care about older browsers, this one did the trick for me.

// [optional] make sure focus is on the elementyourContentEditableElement.focus();// select all the content in the elementdocument.execCommand('selectAll', false, null);// collapse selection to the enddocument.getSelection().collapseToEnd();


There is also another problem.

The Nico Burns's solution works if the contenteditable div doesn't contain other multilined elements.

For instance, if a div contains other divs, and these other divs contain other stuff inside, could occur some problems.

In order to solve them, I've arranged the following solution, that is an improvement of the Nico's one:

//Namespace management idea from http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/(function( cursorManager ) {    //From: http://www.w3.org/TR/html-markup/syntax.html#syntax-elements    var voidNodeTags = ['AREA', 'BASE', 'BR', 'COL', 'EMBED', 'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK', 'MENUITEM', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR', 'BASEFONT', 'BGSOUND', 'FRAME', 'ISINDEX'];    //From: https://stackoverflow.com/questions/237104/array-containsobj-in-javascript    Array.prototype.contains = function(obj) {        var i = this.length;        while (i--) {            if (this[i] === obj) {                return true;            }        }        return false;    }    //Basic idea from: https://stackoverflow.com/questions/19790442/test-if-an-element-can-contain-text    function canContainText(node) {        if(node.nodeType == 1) { //is an element node            return !voidNodeTags.contains(node.nodeName);        } else { //is not an element node            return false;        }    };    function getLastChildElement(el){        var lc = el.lastChild;        while(lc && lc.nodeType != 1) {            if(lc.previousSibling)                lc = lc.previousSibling;            else                break;        }        return lc;    }    //Based on Nico Burns's answer    cursorManager.setEndOfContenteditable = function(contentEditableElement)    {        while(getLastChildElement(contentEditableElement) &&              canContainText(getLastChildElement(contentEditableElement))) {            contentEditableElement = getLastChildElement(contentEditableElement);        }        var range,selection;        if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+        {                range = document.createRange();//Create a range (a range is a like the selection but invisible)            range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range            range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start            selection = window.getSelection();//get the selection object (allows you to change selection)            selection.removeAllRanges();//remove any selections already made            selection.addRange(range);//make the range you have just created the visible selection        }        else if(document.selection)//IE 8 and lower        {             range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)            range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range            range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start            range.select();//Select the range (make it the visible selection        }    }}( window.cursorManager = window.cursorManager || {}));

Usage:

var editableDiv = document.getElementById("my_contentEditableDiv");cursorManager.setEndOfContenteditable(editableDiv);

In this way, the cursor is surely positioned at the end of the last element, eventually nested.

EDIT #1: In order to be more generic, the while statement should consider also all the other tags which cannot contain text. These elements are named void elements, and in this question there are some methods on how to test if an element is void. So, assuming that exists a function called canContainText that returns true if the argument is not a void element, the following line of code:

contentEditableElement.lastChild.tagName.toLowerCase() != 'br'

should be replaced with:

canContainText(getLastChildElement(contentEditableElement))

EDIT #2: The above code is fully updated, with every changes described and discussed