我有一个设置了 contentEditable 属性的 div。
我遇到的麻烦是我输入的第一行没有被包裹在标签中,但后续行却被包裹起来。
所以输入:
qwerty
zxcv
asdfg
结果:
<div class="editable" contentEditable="true">
qwerty
<div>zxcv</div>
<div>asdfg</div>
</div>
所以我需要
qwerty
线变成 <div>qwerty</div>
我在 Chrome 中添加 div 时遇到了问题。仅当您的 div 包含 br 标签时它才有效:
<div class="editable" contentEditable="true">
<div>
<br>
</div>
</div>
@nicholas_r 是对的,删除可编辑div中的最后一个字符也会删除开头添加的div。您可以通过抓住最后一个“退格键”来避免这种情况。使用 jquery 的简单解决方案:
$(".editable").on('keydown', function(event) {
if (event.keyCode === 8 && $(this).html() == '<div><br></div>') {
event.preventDefault();
}
}
如果有帮助,运行以下命令将让您包装未包装的 div 。如果您在按键或类似的情况下触发它,即使用户从 div 中删除所有内容,它也会起作用。
$(".editable").contents().filter(function() {
return this.nodeType == 3;
}).wrap('<div></div>');
如果您从一个内部包含
div
的元素开始,如下所示:
<div class="editable" contentEditable="true">
<div></div>
</div>
您输入的第一行最终将被包裹在
div
中。
更新:
这是特定于浏览器的。虽然在问题发布时它确实在大多数浏览器中工作,但它不一定在最新的浏览器版本中工作。最好的判断方法就是测试一下。
这些解决方案中的大多数在 2017 年的 Chrome(版本 61.x)中对我来说不起作用,并且 Kasper 的答案对我来说似乎太混乱了。
据我所知,在
""
事件上简单检查 <br>
或 input
可以处理所有可能的情况,包括退格、删除和复制粘贴:
$("[contenteditable='true']")
.on("input", function ()
{
var $editable = $(this);
if ($editable.html().trim() === "" || $editable.html().trim() === "<br>")
$editable.html($("<div><br></div>"));
})
.trigger("input");
请注意,我还在初始化时触发
input
事件,以确保初始内容设置正确,但这可能需要也可能不需要,具体取决于您的特定需求。
https://jsfiddle.net/25ujvrn0/
更新:现代化的非 jQuery 替代方案:
document.querySelectorAll("[contenteditable='true']").forEach(editable => {
editable.addEventListener("input", function () {
if (editable.innerHTML.trim() === "" || editable.innerHTML.trim() === "<br>") {
editable.innerHTML = "<div><br></div>";
}
});
editable.dispatchEvent(new Event("input"));
});
我做了类似的事情,它非常有效地确保文本的第一行始终有一个 div 包装器。
这样,如果用户单击该字段及其空白(初始状态),它将在其中放置一个空白,以确保他们的光标位于新 div 内。
$editable.on('focus', function() {
if ($(this).text().trim() === '') {
console.log('editable is blank, insert a div');
$(this).append($('<div> </div>'));
}
});
您可以在保存数据之前删除前导空格(如果重要)。
对于 2017 年或之后遇到过这个问题的人来说,这些答案在 Chrome 中都不适合我,而那些试图提出智能解决方案的人并没有涵盖我的所有案例。
虽然只在Chrome中进行了测试,而且有点粗糙,但这涵盖了3个主要场景:
contenteditable
div 中的内容。contenteditable
或 ctrl+a Delete
删除 ctrl+a Backspace
div 中的所有内容。contenteditable
替换为自己选择的字母来删除 ctrl-a
div 中的所有内容。 一个 JSFiddle 演示。
document.getElementById("editor").addEventListener("keydown", function(e){
if(e.which == 8 && document.getElementById("editor").innerHTML=="<div><br></div>"){ // 8 is backspace
e.preventDefault()
}
if (window.getSelection && e.which == 8 || e.which == 46 && window.getSelection){ // 8 is backspace, 46 is delete
let selection = window.getSelection().toString();
let editorText = document.getElementById("editor").innerText;
if (editorText == selection){
document.getElementById("editor").innerHTML="<div><br></div>"
console.log("prevented a ctrl+a delete")
}
else if (window.getSelection){
let selection = window.getSelection().toString();
let editorText = document.getElementById("editor").innerText;
if (editorText == selection){
document.getElementById("editor").innerHTML="<div>"+e.which+"</div>";
console.log("safely did a ctrl+a " + e.which)
}
}
e.preventDefault()
}
})
背景
这是 OP 的解决方案,可确保内容可编辑 div 中的第一行始终被换行并修复常见错误,例如:
它使用事件侦听器来捕获用户何时聚焦于编辑器以及何时离开。有关更多上下文,请阅读代码中的注释。
代码
下面是代码的完整工作小提琴。我的原始示例可以在这个 JS Fiddle 页面找到。
// On Dom Ready
document.getElementById('editor').addEventListener('focusin', checkEditor);
document.getElementById('editor').addEventListener('focusout', checkEditor);
function checkEditor() {
// Get the DOM element that the user is in
// Allows you to have multiple editos on one page
// Next 3 lines of code from: https://stackoverflow.com/a/1553668/3193156
var e = event || window.event;
var elem = e.target || e.srcElement;
if (elem.nodeType == 3) {
elem = elem.parentNode;
} // Defeat Safari bug
// Grab the elements inner HTML
var html = elem.innerHTML
// If html is empty or does not start with a div see if we need to fix something
if (html == '' || html.substr(0, 5) != '<div>') {
notify('Running editor checks.');
var index;
// Catch bug where a line break is added after a user uses backspace to wipe the editor
if (html.substr(0, 4) == '<br>') {
notify('Removed line break.');
index = html.indexOf('\n');
// If there are any lines after this line break save them
if (index > -1) {
html = html.substr(html.indexOf('\n') + 1);
} else {
html = '';
}
elem.innerHTML = html;
// If everything is ok return otherwise keep processing
if (html != '' && html.substr(0, 5) == '<div>') {
return;
}
}
// Catch bug where first line after erasing editor does not get wrapped
index = html.indexOf('\n');
if (index > -1) {
notify('Wrapped the fist line, did not check the rest.');
html = html = '<div>' + html.substr(0, html.indexOf('\n') + 1) + '</div>' + html.substr(html.indexOf('\n') + 1);
elem.innerHTML = html;
return;
} else {
html = html.trim();
if (html.length > 0) {
notify('Wrapped the fist line');
html = '<div>' + html.trim() + '</div>';
elem.innerHTML = html;
return;
}
}
// If we made it here the editor shold be considered empty, reset it
notify('Editor reset.');
var div = document.createElement('DIV');
div.innerHTML = '<br>';
elem.appendChild(div);
}
}
function notify(msg) {
var div = document.createElement('DIV');
div.innerHTML = msg;
document.getElementById('messages').appendChild(div);
}
#editor {
width: 90%;
height: 200px;
padding: 1rem;
border: 1px solid #000;
}
<div id="editor" contenteditable="true">
</div>
<div id="messages">
</div>
待办事项/缺失
该解决方案回答了OP,但不考虑以下用例:
仅仅简单的
<div></div>
是行不通的。
如果您正在寻找 jQuery 解决方案,这里是代码:
$('.contenteditable').on 'focus', ->
if $(@).text().trim() == ''
$(@).append($('<div><br /></div>'))