Закрытие тегов <ul/"> в contenteditable без добавления лишних тегов
Работа с неупорядоченным (или упорядоченным) списком в contenteditable дает мне головную боль.
Всякий раз, когда я хочу закончить редактирование списка, нажав ENTER дважды, браузер закроет <ul />
, но вставляет тег <p />
(Firefox) или <div />
(Chrome), который содержит <br />
.
Пример здесь
Моя цель - избежать лишних <p />
или <div />
и вместо этого просто закрыть <ul />
.
Я попытался изменить решение Tim Down, которое не позволит браузеру вставлять <p />
или <div />
при нажатии ENTER и вместо этого вставляет чистый тег <br />
.
Пример здесь
К сожалению, при использовании этого решения <ul />
никогда не закрывается браузером, так как только теги <br />
вставлены внутри элемента <li />
.
Итак, мой вопрос:
Как я могу активно закрыть <ul />
, вставив node или вставляя html при нажатии enter на последнем пустом <li />
?
Обновление: Если вопрос неясен: я ищу способ закрыть <ul />
без вставки тегов <p />
или <div />
, но просто вставив старую добрую равнину <br />
вместо
Ответы
Ответ 1
Подобно вашей попытке, мы изменяем контент, когда пользователь нажимает Enter: Демо
if (window.getSelection) { // w3c
$('div').keypress(function (e) {
var sel, node, children, br, range;
if (e.which == 13) {
sel = window.getSelection();
node = $(sel.anchorNode);
children = $(sel.anchorNode.childNodes);
// if nothing is selected and the caret is in an empty <li>
// (the browser seems to insert a <br> before we get called)
if (sel.isCollapsed && node.is('li') && (!children.length ||
(children.length == 1 && children.first().is('br')))) {
e.preventDefault();
// if the empty <li> is in the middle of the list,
// move the following <li> to a new list
if (!node.is(':last-child')) {
node.parent().clone(false)
.empty()
.insertAfter(node.parent())
.append(node.nextAll());
}
// insert <br> after list
br = $('<br>').insertAfter(node.parent());
// move caret to after <br>
range = document.createRange();
range.setStartAfter(br.get(0));
range.setEndAfter(br.get(0));
sel.removeAllRanges();
sel.addRange(range);
// remove <li>
node.remove();
}
}
});
} else if (document.selection) { // internet explorer
$('div').keypress(function (e) {
var range, node, children;
if (e.which == 13) {
range = document.selection.createRange();
node = $(range.parentElement());
children = $(range.parentElement().childNodes);
// if nothing is selected and the caret is in an empty <li>
// (the browser seems to insert a <br> before we get called)
if (!range.htmlText.length && node.is('li') && (!children.length ||
(children.length == 1 && children.first().is('br')))) {
e.preventDefault();
// if the empty <li> is in the middle of the list,
// move the following <li> to a new list
if (!node.is(':last-child')) {
node.parent().clone(false)
.empty()
.insertAfter(node.parent())
.append(node.nextAll());
}
// insert <br> after list
br = $('<br>').insertAfter(node.parent());
// move caret to after <br>
range = document.body.createTextRange();
range.moveToElementText(br.get(0));
range.collapse(false);
range.select();
// remove <li>
node.remove();
}
}
});
}
Обратите внимание, что это не относится к случаю, когда пользователь выбрал что-то перед нажатием Enter. Если вы хотите обработать этот случай, вам нужно выяснить, выбрал ли пользователь все содержимое <li>
(это не похоже на тривиальную задачу), и если это так, удалите содержимое и обработайте его так же, как если бы пользователь нажал Enter в пустом <li>
.
Ответ 2
Я понимаю ваш вопрос, но неясно, почему такое требование существует. Это может помочь прояснить этот аспект.
Независимо, вот одна идея. Почему бы не заменить или удалить теги <div>
и <p>
.
$(document).ready(function(){
$("div").keyup(function(evt) {
$("div, p").filter( function() {
$this = $(this);
return !($.trim($(this).text()).length);
}).replaceWith('<br />');
});
});
Рабочий пример: http://jsfiddle.net/4xyR2/9/
Одна проблема, которую я замечаю, касается того, как contenteditable
влияет на курсор, используя указанное выше решение. Я думаю, что Chrome и Firefox требуют <div>
и <p>
, чтобы они могли отслеживать, где находится курсор в <div>
. Это происходит из моих наблюдений во время тестирования, а не из глубокого понимания того, как браузер интерпретирует contenteditable
.
Chrome (24.0.1312.52 m), похоже, не нравится замене. Я вижу странное расположение курсора, когда я его тестирую, но он работает. Firefox (17.0.1) отлично справляется с заменой.
Ответ 3
Лучшее, что я смог тренировать без Javascript, было
HTML:
<div contenteditable="true">
<div>asdfasdf</div>
</div>
LESS:
div[contenteditable=true] {
outline: none;
&>p,
&>div {
margin: 0 0 0 30px;
display: list-item;
}
}
bin http://jsbin.com/idekix/6/edit
Но здесь есть некоторые недостатки,
-
первый и очень большой FireFox 18.0.1 на Win8 вместо вставки тегов div
или p
в качестве новой строки использует нечто вроде double br
,
-
вторая произошла, когда вы удаляете все и запускаете тип и нажимаете enter, поведение разных браузеров по-разному, Chrome вставляет первую строку в виде простого текстового контейнера node из contenteditable
, а затем помещает каждую новую строку в div
Opera 12.12 справится с этим лучше, и он никогда не позволяет удалить последний div
node, IE9-10, начинающий прыгать, и после того, как вы снова начали использовать теги p
вместо div
(что даже не такое катастрофа, просто несогласованность), но тем не менее FireFox еще не пытается изменить поведение br
. Также пытаясь проверить IE7-8 с режимом отладки IE10, и это то же самое, что и Opera, хотя это не настоящие браузеры.
-
третий, который позволяет вставлять пустые узлы, говоря об этом, хорошая работа, выполняемая Opera, см. в этом бункере в Opera http://jsbin.com/uxesez/1/edit, разметка здесь ближе к оригиналу, и она не позволяет вам вставлять пустой li
.
Тем не менее, все тесты проводились на 64-битной платформе Windows 8 с Chrome 24.0.1312.56 m, FireFox 18.0.1, Opera 12.12 и IE10 с эмуляцией старых IE с помощью панели отладки IE. Признайте, что на других платформах/версиях все может быть разным.
Подведите итог выше, contenteditable
имеет очень хороший диапазон поддержки браузера http://caniuse.com/contenteditable, но все же у каждого браузера есть собственная реализация, а другая отдельный вопрос почему. Таким образом, без перекрестного вызова JavaScrpt вы не сможете использовать его. Кроме того, как моя личная идея contenteditable
Основная идея более подходит, как легкая замена iframe
, когда она используется как элемент почти каждого богатого текстового редактора в браузере.
UPD из источника Mozilla https://developer.mozilla.org/en-US/docs/HTML/Content_Editable
В HTML5 любой элемент может быть доступен для редактирования. Эта функция была введена давно, но теперь стандартизована WHATWG (html current spec). С помощью некоторых обработчиков событий JavaScript вы можете преобразовать свою веб-страницу в полнофункциональный богатый текстовый редактор.
Следующий шаг - это просто мое скромное предложение. В вашем случае очень удобно, по моему мнению, использовать реальные редактируемые поля. Очень большая версия прототипа, которую я сделал в этом бункере http://jsbin.com/ojiyok/7/edit, все еще есть вещи для улучшения, но похоже, что он имеет более предсказуемое поведение, чем реальный conteneditables
, и здесь вы можете изменить пользовательский интерфейс в соответствии со своими потребностями во всех браузерах одновременно.