Christian Wansart: Cursor wird im contenteditable-div nicht richtig gesetzt (mit JavaScript)

Beitrag lesen

Hallo,

ich versuche gerade einen einfachen Editor zu entwickeln. Ich bekomme aus einer anderen Software mit, an welcher Stelle ein Fehler war. Also möchte ich dieses Zeichen hervorheben.

Ich habe also ein div mit dem Attribut contenteditable="true". Nun habe ich einen EventHandler auf keypress registriert und möchte das Zeichen entsprechend manuell einfügen und den Cursor dann wieder an die richtige Stelle packen. Das hier ist mein Ansatz:

$(function() {
    $('.definition').on('keypress', function(e) {
        e.preventDefault();
        const code = $(this).text();
        const pos = getCaretPosition(this);
        //const span = '<span style="color:red">' + e.key + '</span>';
        const span = e.key;
        const newCode = [code.slice(0, pos), span, code.slice(pos)].join('');
        this.innerHTML = newCode;

        // Move the cursor after the inserted element.
        const sel = window.getSelection();
        let range = document.createRange();
        range.setStart(this.firstChild, pos+1);
        range.collapse(true);
        sel.removeAllRanges();
    });
});

// Diese Funktion stammt von Stackoverflow, da war jemand so nett und hat
// sie schon entwickelt.
function getCaretPosition(editableDiv) {
    var caretPos = 0;
    var sel;
    var range;

    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.rangeCount) {
            range = sel.getRangeAt(0);
            if (range.commonAncestorContainer.parentNode == editableDiv) {
                caretPos = range.endOffset;
            }
        }
    } else if (document.selection && document.selection.createRange) {
        range = document.selection.createRange();
        if (range.parentElement() == editableDiv) {
            var tempEl = document.createElement("span");
            editableDiv.insertBefore(tempEl, editableDiv.firstChild);
            var tempRange = range.duplicate();
            tempRange.moveToElementText(tempEl);
            tempRange.setEndPoint("EndToEnd", range);
            caretPos = tempRange.text.length;
        }
    }
    return caretPos;
}

Nun funktioniert der Code so wunderbar. Aber ich möchte gerne einzelne Zeichen rot färben, weswegen ich mir dachte, dass ich ein span um das Zeichen packe und so einfüge. Wenn ich nun die Zeile 6 ein und Zeile 7 auskommentiere und es ausprobiere, bekomme ich einen Fehler:

Uncaught DOMException: Failed to execute 'setStart' on 'Range': The offset 2 is larger than or equal to the node's length (1).
    at HTMLDivElement.<anonymous> (http://localhost/XBlockDefinitionReader/js/src/author_view.js:14:15)
    at HTMLDivElement.dispatch (http://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js:3:6466)
    at HTMLDivElement.r.handle (http://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js:3:3241)`

Ich bin mir nun nicht sicher, wie ich das Problem lösen soll. Ich denke, ich verstehe auch nicht so recht was das Problem hier ist. Klar, durch das span-Element wird die Variable code länger, aber das sollte doch eher dazu führen, dass der Cursor einfach an anderer Stelle eingefügt wird?

Habt ihr eine Idee, wie ich das lösen könnte?

Beste Grüße
Christian