Fügen Sie HTML bei Caret in ein inhaltsbearbeitbares Div ein

74

Ich habe ein Div mit inhaltsbearbeitbarem Satz und ich nehme den Tastendruck mit jquery auf, um PreventDefault () aufzurufen, wenn die Eingabetaste gedrückt wird. Ähnlich wie bei dieser Frage, bei der Text am Cursor eingefügt wird, möchte ich HTML direkt einfügen. Der Kürze halber sagen wir, es ist ein br-Tag. Die Verwendung der Antwort auf die obige Frage funktioniert im IE tatsächlich, da die range.pasteHTML-Methode verwendet wird. In anderen Browsern wird das br-Tag jedoch als einfacher Text und nicht als HTML angezeigt. Wie kann ich die Antwort ändern, um HTML und nicht Text einzufügen?

Niall
quelle

Antworten:

181

In den meisten Browsern können Sie die insertNode() Methode des Bereichs verwenden, den Sie aus der Auswahl erhalten. In IE <9 können Sie verwenden pasteHTML(), wie Sie erwähnt haben. Im Folgenden finden Sie eine Funktion, die dies in allen gängigen Browsern ermöglicht. Wenn der Inhalt bereits ausgewählt ist, wird er ersetzt, sodass dies effektiv ein Einfügevorgang ist. Außerdem habe ich Code hinzugefügt, um das Caret nach dem Ende des eingefügten Inhalts zu platzieren.

jsFiddle: http://jsfiddle.net/jwvha/1/

Code:

function pasteHtmlAtCaret(html) {
    var sel, range;
    if (window.getSelection) {
        // IE9 and non-IE
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();

            // Range.createContextualFragment() would be useful here but is
            // only relatively recently standardized and is not supported in
            // some browsers (IE9, for one)
            var el = document.createElement("div");
            el.innerHTML = html;
            var frag = document.createDocumentFragment(), node, lastNode;
            while ( (node = el.firstChild) ) {
                lastNode = frag.appendChild(node);
            }
            range.insertNode(frag);

            // Preserve the selection
            if (lastNode) {
                range = range.cloneRange();
                range.setStartAfter(lastNode);
                range.collapse(true);
                sel.removeAllRanges();
                sel.addRange(range);
            }
        }
    } else if (document.selection && document.selection.type != "Control") {
        // IE < 9
        document.selection.createRange().pasteHTML(html);
    }
}

UPDATE 21. AUGUST 2013

Wie in den Kommentaren angefordert, finden Sie hier ein aktualisiertes Beispiel mit einem zusätzlichen Parameter, der angibt, ob der eingefügte Inhalt ausgewählt werden soll oder nicht.

Demo: http://jsfiddle.net/timdown/jwvha/527/

Code:

function pasteHtmlAtCaret(html, selectPastedContent) {
    var sel, range;
    if (window.getSelection) {
        // IE9 and non-IE
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();

            // Range.createContextualFragment() would be useful here but is
            // only relatively recently standardized and is not supported in
            // some browsers (IE9, for one)
            var el = document.createElement("div");
            el.innerHTML = html;
            var frag = document.createDocumentFragment(), node, lastNode;
            while ( (node = el.firstChild) ) {
                lastNode = frag.appendChild(node);
            }
            var firstNode = frag.firstChild;
            range.insertNode(frag);

            // Preserve the selection
            if (lastNode) {
                range = range.cloneRange();
                range.setStartAfter(lastNode);
                if (selectPastedContent) {
                    range.setStartBefore(firstNode);
                } else {
                    range.collapse(true);
                }
                sel.removeAllRanges();
                sel.addRange(range);
            }
        }
    } else if ( (sel = document.selection) && sel.type != "Control") {
        // IE < 9
        var originalRange = sel.createRange();
        originalRange.collapse(true);
        sel.createRange().pasteHTML(html);
        if (selectPastedContent) {
            range = sel.createRange();
            range.setEndPoint("StartToStart", originalRange);
            range.select();
        }
    }
}
Tim Down
quelle
1
@ think123: Sie können eine Funktion wie die folgende verwenden, um zu überprüfen, ob die Auswahl in einem bestimmten Knoten enthalten ist: stackoverflow.com/a/8340432/96100
Tim Down
1
@tundoopani: Das liegt daran, dass jsFiddle die pasteHtmlAtCaret ()Funktion in einem onloadHandler platziert, in dem nichts anderes sie sehen kann. Siehe jsfiddle.net/jwvha/211 für das Update .
Tim Down
5
@Matt: Die Verwendung document.execCommand()funktioniert normalerweise mit dem Rückgängig-Stapel des Browsers, sodass Sie ihn möglicherweise verwenden können document.execCommand("InsertHTML", false, "<b>Some bold text</b>"). Ich habe jedoch nicht getestet, dass das Rückgängigmachen damit noch funktioniert, und der IE unterstützt diesen Befehl nicht. Schließlich ist eine UndoManager-Spezifikation in Arbeit, die langfristig die Lösung dafür sein wird und in Browsern implementiert wird: dvcs.w3.org/hg/undomanager/raw-file/tip/undomanager.html
Tim Down
1
@Ced, das Fensterobjekt des Iframes. Verzeihen Sie mir, dass ich auf diese Site verwiesen habe , aber dies ist das einfachste Beispiel, das ich in den 10 Sekunden gefunden habe, in denen ich nach Ihnen gesucht habe ...
Gdoron unterstützt Monica
1
6 Jahre später und dies ist immer noch die perfekte Lösung. Vielen Dank.
FullStackFool
13
var doc = document.getElementById("your_iframe").contentWindow.document;

// IE <= 10
if (document.selection){
    var range = doc.selection.createRange();
        range.pasteHTML("<b>Some bold text</b>");

// IE 11 && Firefox, Opera .....
}else if(document.getSelection){
    var range = doc.getSelection().getRangeAt(0);
    var nnode = doc.createElement("b");
    range.surroundContents(nnode);
    nnode.innerHTML = "Some bold text";
};
user3126867
quelle
1

Wenn Sie schnell lesen und hoffen, nicht vom Thema abzukommen, finden Sie hier einen Titel für diejenigen, die wie ich Code auf der Cursorebene eines div einfügen müssen:

document.getElementById('editeur').contentWindow.document.execCommand('insertHTML', false, '<br />');

'editeur' ist iframe:

<iframe id="editeur" src="contenu_editeur_wysiwyg.php">
</iframe>

contenu_editeur_wysiwyg.php:

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div>
</div>
</body>
</html>

vergiss nicht:

document.getElementById('editeur').contentDocument.designMode = "on";
delaio
quelle
-2
var r = getSelection().getRangeAt(0);
r.insertNode(r.createContextualFragment('<b>Hello</b>'));

//select this range
getSelection().removeAllRanges();
getSelection().addRange(r);
//collapse to end/start 
getSelection().collapseToEnd() 
Alex G.
quelle
Dies funktioniert nicht für Inhalte, die beispielsweise in der Mitte einer Eingabe hinzugefügt wurden. Die Auswahl nach dem Einfügen wird immer zum Ende verschoben.
Mario Estrada