Il y a aussi un autre problème.
La solution de Nico Burns fonctionne si la div contenteditable
ne contient pas d'autres éléments multilignes.
Par exemple, si une div contient d'autres divs, et que ces autres divs contiennent d'autres éléments à l'intérieur, certains problèmes pourraient survenir.
Pour les résoudre, j'ai élaboré la solution suivante, qui est une amélioration de celle de Nico:
//Idée de gestion de l'espace de noms de http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/
(function( cursorManager ) {
//De : 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'];
//De : 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;
}
//Idée de base de : https://stackoverflow.com/questions/19790442/test-if-an-element-can-contain-text
function canContainText(node) {
if(node.nodeType == 1) { //est un nœud élément
return !voidNodeTags.contains(node.nodeName);
} else { //n'est pas un nœud élément
return false;
}
};
function getLastChildElement(el){
var lc = el.lastChild;
while(lc && lc.nodeType != 1) {
if(lc.previousSibling)
lc = lc.previousSibling;
else
break;
}
return lc;
}
//Basé sur la réponse de Nico Burns
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();//Créer une plage (une plage est comme la sélection mais invisible)
range.selectNodeContents(contentEditableElement);//Sélectionner tout le contenu de l'élément avec la plage
range.collapse(false);//réduire la plage au point de fin. false signifie se réduire à la fin plutôt qu'au début
selection = window.getSelection();//obtenir l'objet de sélection (vous permet de modifier la sélection)
selection.removeAllRanges();//supprimer les sélections déjà effectuées
selection.addRange(range);//faire de la plage que vous venez de créer la sélection visible
}
else if(document.selection)//IE 8 et inférieur
{
range = document.body.createTextRange();//Créer une plage (une plage est comme la sélection mais invisible)
range.moveToElementText(contentEditableElement);//Sélectionner tout le contenu de l'élément avec la plage
range.collapse(false);//réduire la plage au point de fin. false signifie se réduire à la fin plutôt qu'au début
range.select();//Sélectionnez la plage (rendez-la visible la sélection
}
}
}( window.cursorManager = window.cursorManager || {}));
Utilisation:
var editableDiv = document.getElementById("my_contentEditableDiv");
cursorManager.setEndOfContenteditable(editableDiv);
Ainsi, le curseur est sûrement positionné à la fin du dernier élément, éventuellement imbriqué.
MODIFICATION #1: Afin d'être plus générique, l'instruction while
devrait également considérer toutes les autres balises qui ne peuvent pas contenir de texte. Ces éléments sont appelés éléments vides, et dans cette question il y a quelques méthodes sur la façon de tester si un élément est vide. Donc, en supposant qu'il existe une fonction appelée canContainText
qui renvoie true
si l'argument n'est pas un élément vide, la ligne de code suivante:
contentEditableElement.lastChild.tagName.toLowerCase() != 'br'
doit être remplacée par:
canContainText(getLastChildElement(contentEditableElement))
MODIFICATION #2: Le code ci-dessus est entièrement mis à jour, avec tous les changements décrits et discutés