webentwicklung-frage-antwort-db.com.de

Verschieben des Cursors an das Ende der inhaltbaren Entität

Ich muss die Einfügemarke an das Ende des contenteditable-Knotens wie in Google Mail-Notizen-Widget verschieben.

Ich habe Threads in StackOverflow gelesen, aber diese Lösungen basieren auf der Verwendung von Eingaben und funktionieren nicht mit contenteditable-Elementen.

66
avsej

Es gibt auch ein anderes Problem.

Die Lösung von Nico Burns funktioniert, wenn contenteditable div keine anderen mehrzeiligen Elemente enthält.

Wenn zum Beispiel ein div andere divs enthält und diese anderen divs anderes Material enthalten, können einige Probleme auftreten.

Um sie zu lösen, habe ich folgende Lösung arrangiert, nämlich eine Verbesserung der Nico s:

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

Verwendungszweck:

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

Auf diese Weise wird der Cursor sicher am Ende des letzten Elements positioniert und eventuell verschachtelt.

EDIT # 1: Um generischer zu sein, sollte die while-Anweisung auch alle anderen Tags berücksichtigen, die keinen Text enthalten können. Diese Elemente heißen void elements, und in diese Frage gibt es einige Methoden, um zu testen, ob ein Element ungültig ist. Angenommen, es gibt eine Funktion namens canContainText, die true zurückgibt, wenn das Argument kein ungültiges Element ist, die folgende Codezeile:

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

sollte ersetzt werden durch:

canContainText(getLastChildElement(contentEditableElement))

EDIT # 2: Der obige Code wird vollständig aktualisiert, wobei alle Änderungen beschrieben und diskutiert werden

22
Vito Gentile

Die Geowa4-Lösung funktioniert für einen Textbereich, jedoch nicht für ein inhaltsfähiges Element.

Diese Lösung dient zum Verschieben des Caret-Elements an das Ende eines inhaltsfähigen Elements. Es sollte in allen Browsern funktionieren, die contenteditable unterstützen.

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
    }
}

Es kann durch folgenden Code verwendet werden:

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

Es ist möglich, den Cursor durch den Bereich auf das Ende zu setzen:

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; 
}
2
Andrew

Wenn Sie sich nicht für ältere Browser interessieren, hat dieser für mich den Trick getan.

// [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();
1
Juank

Bewegen des Cursors an das Ende des bearbeitbaren Bereichs als Reaktion auf das Fokusereignis:

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

Und nenne es im Event-Handler (hier reagieren):

onFocus={(e) => this.moveCursorToEnd(e.target)}} 
0
Maxim Saplin

Ich hatte ein ähnliches Problem beim Versuch, ein Element editierbar zu machen. In Chrome und FireFox war dies möglich, aber in FireFox ging das Caret entweder an den Anfang der Eingabe oder nach dem Ende der Eingabe ein Leerzeichen. Sehr verwirrend für den Endbenutzer, denke ich, der versucht, den Inhalt zu bearbeiten.

Ich habe keine Lösung gefunden, die mehrere Dinge versucht. Das Einzige, was für mich funktionierte, war "das Problem zu umgehen", indem eine einfache alte Texteingabe in INSIDE my eingefügt wurde. Jetzt gehts. Es scheint, als sei "inhaltsbearbeitbar" immer noch Edge Edge-Technologie, die je nach Kontext möglicherweise nicht so funktioniert, wie Sie möchten. 

0
Panu Logic