Question

Je dois passer caret à la fin du nœud contenteditable comme sur Gmail widget Notes.

Je lis les discussions sur StackOverflow, mais ces solutions sont basées sur l'utilisation des intrants et ils ne fonctionne pas avec des éléments contenteditable.

Était-ce utile?

La solution

Il y a aussi un autre problème.

La solution de Nico Burns, fonctionne si le div contenteditable ne contient pas d'autres éléments multilined.

Par exemple, si un div contient d'autres divs, et ces autres divs contiennent d'autres choses à l'intérieur, pourrait se produire quelques problèmes.

Pour les résoudre, je me suis arrangé la solution suivante, qui est une amélioration de la Nico l » un:

//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 || {}));

Utilisation:

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

De cette façon, le curseur est positionné sûrement à la fin du dernier élément, éventuellement imbriqués.

EDIT # 1 : Pour être plus générique, l'instruction while doit également tenir compte de toutes les autres balises qui ne peuvent pas contenir du texte. Ces éléments sont appelés éléments vides , et cette question il existe 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 retourne true si l'argument n'est pas un élément vide, la ligne de code suivante:

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

devrait être remplacé par:

canContainText(getLastChildElement(contentEditableElement))

EDIT # 2 : Le code ci-dessus est entièrement mis à jour, avec tous les changements décrits et discutés

Autres conseils

La solution de Geowa4 fonctionnera pour un textarea, mais pas pour un élément contenteditable.

Cette solution est destiné à déplacer le curseur à l'extrémité d'un élément contenteditable. Il devrait fonctionner dans tous les navigateurs qui prennent en charge 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
    }
}

Il peut être utilisé par un code similaire à:

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

Si vous ne se soucient pas de navigateurs plus anciens, celui-ci a fait l'affaire pour moi.

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

Il est possible de faire ensemble curseur jusqu'à la fin à travers la gamme:

setCaretToEnd(target/*: HTMLDivElement*/) {
  const range = document.createRange();
  const sel = window.getSelection();
  range.selectNodeContents(target);
  range.collapse(false);
  sel.removeAllRanges();
  sel.addRange(range);
  target.focus();
  range.detach(); // optimization

  // set scroll to the end if multiline
  target.scrollTop = target.scrollHeight; 
}

J'ai eu un problème similaire en essayant de faire un élément modifiable. Il a été possible dans Chrome et FireFox, mais dans FireFox le caret soit allé au début de l'entrée ou il est allé un espace après la fin de l'entrée. Très déroutant pour l'utilisateur final, je pense, en essayant de modifier le contenu.

J'ai trouvé aucune solution à essayer plusieurs choses. La seule chose qui a fonctionné pour moi était de « faire le tour du problème » en mettant un dans mon ancien texte d'entrée ordinaire. Maintenant ça marche. On dirait saigne encore tech bord « contenu modifiable », qui peut travailler ou non que vous le souhaitez à travailler, en fonction du contexte.

En déplaçant le curseur à la fin de l'échelle modifiable en réponse à concentrer l'action:

  moveCursorToEnd(el){
    if(el.innerText && document.createRange)
    {
      window.setTimeout(() =>
        {
          let selection = document.getSelection();
          let range = document.createRange();

          range.setStart(el.childNodes[0],el.innerText.length);
          range.collapse(true);
          selection.removeAllRanges();
          selection.addRange(range);
        }
      ,1);
    }
  }

Et l'appeler dans le gestionnaire d'événements (React ici):

onFocus={(e) => this.moveCursorToEnd(e.target)}} 
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top