Вставить html под курсором в контентный div

У меня есть div с contenteditable set, и я захватываю нажатие клавиши с помощью jquery для вызова preventDefault () при нажатии клавиши ввода. Подобно этому вопросу, который вставляет текст в курсор , Я хотел бы вставить непосредственно html, для краткости назовем его тег br. Использование ответа на вопрос выше действительно работает в IE, поскольку он использует метод range.pasteHTML, но в других браузерах тег br будет отображаться как простой текст, а не как html. Как я мог изменить ответ, чтобы вставить html, а не текст?


person Niall    schedule 14.07.2011    source источник


Ответы (3)


В большинстве браузеров вы можете использовать метод insertNode() диапазона, который вы получаете из выбора. В IE ‹9 вы можете использовать pasteHTML(), как вы упомянули. Ниже приведена функция, позволяющая сделать это во всех основных браузерах. Если содержимое уже выбрано, оно заменяется, так что это фактически операция вставки. Кроме того, я добавил код, чтобы поместить курсор после конца вставленного содержимого.

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

Код:

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

ОБНОВЛЕНИЕ 21 АВГУСТА 2013 ГОДА

Как указано в комментариях, вот обновленный пример с дополнительным параметром, который указывает, следует ли выбирать вставленный контент.

Демо: http://jsfiddle.net/timdown/jwvha/527/

Код:

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();
        }
    }
}
person Tim Down    schedule 14.07.2011
comment
@Tim Просто интересно, поскольку я бы не хотел, чтобы пользователи добавляли html повсюду (поскольку функция является кнопкой), как можно позволить этой работе только в определенном div или элементе? - person Lucas; 28.08.2012
comment
@ think123: вы можете использовать такую ​​функцию, чтобы проверить, что выделение содержится в конкретном узле: stackoverflow.com/a/8340432/ 96100 - person Tim Down; 07.09.2012
comment
@TimDown совместим с jQuery? (функция isOrContains) - person Lucas; 08.09.2012
comment
@ think123: Все совместимо с jQuery: jQuery - это просто библиотека. - person Tim Down; 08.09.2012
comment
@TimDown Я пробовал использовать написанный вами код (jsfiddle.net/jwvha/1), но он не работает, когда загружена библиотека jQuery (jsfiddle.net/jwvha/209). Есть идеи, почему это происходит? Как я могу это исправить? Спасибо! Изменить: когда jquery загружен, Chrome выдает это консольное сообщение при нажатии кнопки: Uncaught ReferenceError: pasteHtmlAtCaret не определен - person tundoopani; 19.09.2012
comment
@tundoopani: Это потому, что jsFiddle помещает функцию pasteHtmlAtCaret () в обработчик onload, где ее никто не видит. См. Исправление jsfiddle.net/jwvha/211. - person Tim Down; 19.09.2012
comment
Спасибо, Тим, но как мы сохраняем фокус на измененной области после внутреннего изменения? Например, редактор SO, когда мы выделяем текст и нажимаем на B, текст все еще выделяется или снова становится выбранным (выделенным). - person Vahid; 22.08.2013
comment
@Vahid: Вот демонстрация jsFiddle с улучшенной версией функции, в которой есть флаг, позволяющий выбирать вставленный контент: jsfiddle.net/jwvha/468 - person Tim Down; 22.08.2013
comment
По какой-то причине rang.insertNode (frag) в iFrame у меня не сработал, но, похоже, у меня другая проблема. Однако, когда я изолирую проблему, она выглядит работающей. jsfiddle.net/mELCy - person Laith Shadeed; 16.12.2013
comment
Спасибо @TimDown! Есть ли способ заставить браузер уважать отмену / повтор при вставке html в курсор? - person Matt; 15.01.2014
comment
@Matt: Использование document.execCommand() обычно работает со стеком отмены браузера, поэтому вы можете использовать document.execCommand("InsertHTML", false, "<b>Some bold text</b>"). Однако я не тестировал, что отмена будет работать с этим, а IE не поддерживает эту команду. Наконец, в разработке есть спецификация UndoManager, которая будет решением этой проблемы в долгосрочной перспективе и начинает внедряться в браузерах: dvcs.w3.org/hg/undomanager/raw-file/tip/undomanager.html - person Tim Down; 15.01.2014
comment
Большое спасибо @TimDown! Оказывается, команда undo / redo работает с insertHTML :-), только надоедливый IE не получит встроенную функцию undo / redo: - / - person Matt; 23.01.2014
comment
@Matt: в IE 11 есть несколько новых команд отмены, которые могут помочь. См. stackoverflow.com/a/8544210/96100 и stackoverflow.com/a/20239519/96100 - person Tim Down; 23.01.2014
comment
@TimDown, пожалуйста, скажите мне, почему IE настаивает на том, чтобы курсор оставался перед вставленным узлом? Мне очень нужно, чтобы оно появилось после. FF и Chrome, похоже, справляются с этим нормально. Какие-либо предложения? - person scniro; 16.05.2014
comment
@scniro: Какая версия IE? - person Tim Down; 27.05.2014
comment
Спасибо, Тим, все сработало отлично! Мне просто нужно было немного скорректировать код для поддержки элементов из <iframe>. Моя версия здесь - person gdoron is supporting Monica; 06.07.2014
comment
Почему бы не использовать document.execCommand("InsertHTML") для других браузеров, кроме IE? (Это лучше для функций отмены / повтора) - person Taha Jahangir; 27.02.2015
comment
@TahaJahangir: Это может быть хорошей идеей, но я не уверен, насколько последовательно она реализована. - person Tim Down; 27.02.2015
comment
@TimDown, когда я добавляю еще один тег div и, прежде чем нажимать кнопку, нажимаю на добавленный тег div, а затем после нажатия кнопки в этом сценарии выше код работает некорректно. Можете ли вы решить это? Спасибо. - person Prashant16; 23.03.2015
comment
@gdoron в примере, который вы опубликовали выше, есть параметр с именем window. Что мне дать? Идентификатор iframe что ли? Я не привык к js. - person Ced; 06.09.2015
comment
@Ced, оконный объект iframe. Простите меня за ссылку на этот сайт, но это самый простой пример, который я нашел в 10 сек, я искал тебя ... - person gdoron is supporting Monica; 06.09.2015
comment
Привет, Джим, я хочу использовать этот код для совместимого поведения новой строки (введите событие нажатия клавиши) во всех браузерах. Я добавляю ‹p› ‹br› ‹/p› при вводе после некоторых элементов управления. Но ваш код добавляет ‹p› ‹br› ‹/p› вложенным образом. Как я могу это решить? - person horse; 24.04.2016
comment
Привет, Тим, когда внутри тега b ничего нет, можно сказать, что он не работает, я имею в виду, когда пользователь печатает - текст не отображается жирным шрифтом. Не могли бы вы помочь мне с этим. - person Ujjwal Kumar Gupta; 06.01.2017
comment
Зачем вам нужен createDocumentFragment ()? Что, если мы просто используем range.insertNode (el.firstChild); codepen.io/greensuisse/pen/brELMa - person greensuisse; 30.07.2017
comment
@greensuisse: может быть несколько элементов для вставки, и вы должны вставить только первый. codepen.io/anon/pen/YxqBvX - person Tim Down; 01.08.2017
comment
кто-нибудь заставил это работать с document.execCommand('insertHtml') И поставить курсор после вставленного HTML? когда я вставляю элемент html, который contenteditable="false", курсор проглатывается, и я даже не могу его переместить. Проблема с document.execCommand заключается в том, что вы не получаете ссылку на вновь вставленный элемент. - person w.stoettinger; 23.10.2017
comment
Очень хороший скрипт, но возникает проблема, когда содержимое редактируемого поля div больше, чем это поле. Не могли бы вы помочь? stackoverflow.com/questions/48224189/ Спасибо! - person Baylock; 15.01.2018
comment
Есть ли способ заставить эту функцию работать, передав атрибут id объекта contenteditable? - person Sangar82; 13.02.2018
comment
@TimDown, как мы можем добавить новый текст за пределами ‹b›, потому что я не хочу добавлять следующий текст внутри полужирного тега ...? - person zulqarnain; 13.08.2019
comment
Это не работает в IE11. IE11 возвращает 0 на sel.rangeCount - person Alex from Jitbit; 26.08.2019
comment
@Alex: работает для меня, но он полагается на контент, имеющий фокус. - person Tim Down; 27.08.2019
comment
@TimDown, это проблема, я отвечаю на click (обычно кнопки панели инструментов wysiwyg-editor находятся за пределами области редактирования) - person Alex from Jitbit; 27.08.2019
comment
@Alex: Возможны следующие варианты: 1) использовать вместо этого событие mousedown и предотвратить действие нажатия кнопки по умолчанию; 2) сделать кнопку панели инструментов недоступной для выбора или 3) сохранить выбор перед нажатием кнопки панели инструментов (возможно, через событие mousedown) и восстановить его после (но до выполнения вставки) - person Tim Down; 27.08.2019
comment
@TimDown при вставке кнопки как html курсор или каретка размещаются внутри текста кнопки. У вас есть какое-нибудь решение? Спасибо. Вот ссылка - person shawn batra; 11.12.2020
comment
Мой не вставляет курсор в позицию. @ TimDown Вы что-нибудь знаете об этом - person shawn batra; 19.02.2021
comment
@shawnbatra: У меня отлично работает. Какой браузер? - person Tim Down; 08.03.2021
comment
@TimDown Спасибо за ответ. Я заставляю его работать, но в Firefox я вставил кнопку в позицию курсора, а затем я не смог написать перед кнопкой (не могу поместить курсор при запуске, если кнопка является первым элементом) Вот скрипка - person shawn batra; 08.03.2021

читая быстро и надеясь не выйти за рамки темы, вот трек для тех, кому, как и мне, нужно вставить код на уровне курсора div:

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

editeur - это 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>

не забывай:

document.getElementById('editeur').contentDocument.designMode = "on";
person delaio    schedule 18.08.2019

person    schedule
comment
Это не сработает для контента, добавленного в середине ввода, например, выделение после вставки всегда будет перемещаться в конец. - person Mario Estrada; 27.08.2020