防止contenteditable在按回车键时添加<div>标签 - Chrome

183

我有一个contenteditable元素,每当我输入一些内容并按下ENTER键时,它就会创建一个新的<div>并将新行文本放在里面。我不喜欢这个方式。

是否有可能阻止这种情况发生,或者至少用<br>替换它?

这里是演示http://jsfiddle.net/jDvau/

注意:在firefox中不存在此问题。


1
Firefox添加<br>,Chrome没有,但在修复样式后,额外的<div>不会破坏左侧填充。问题是为什么你不喜欢它?认为这是br... http://jsfiddle.net/jDvau/1/ 另外,您可以使用DOMSubtreeModified事件捕获这些<div>并将其删除。 - ViliusL
1
对我来说,Blake Plumb的解决方案是最简单且最好的。 - svassr
相似问题:https://dev59.com/qW025IYBdhLWcg3wZ1Ny - sv_in
1
@svassr 这不是重点,使用它的不是你或我,而是可能连什么是班次都不知道的客户。 - iConnor
2
确实,它改变了一切。话虽如此,这是一种常见的行为,一个小的帮助信息也不会有害。"授人以鱼不如授人以渔,教会一个人钓鱼,你就让他终身受益。" - svassr
显示剩余4条评论
25个回答

183

试试这个:

$('div[contenteditable]').keydown(function(e) {
    // trap the return key being pressed
    if (e.keyCode === 13) {
        // insert 2 br tags (if only one br tag is inserted the cursor won't go to the next line)
        document.execCommand('insertHTML', false, '<br/>');
        // prevent the default behaviour of return key pressed
        return false;
    }
});

点击此处查看演示


1
但我在这里发现了一点不同。将光标放在空白行上(在“键入一些内容”上方),然后按Enter键。现在,光标位于“Type”之前,而不是新的空白行上。 - Andrew
3
在IE11中,此方法不起作用,因为它不支持insertHTML。请参见下面的答案! - webprogrammer
5
如果您将光标放置在字符之间,例如“Ty[cursor]pe some stuff”,然后按回车键,会多出一行。 - Chandrew
16
答案不可接受。解决方案是只有一个<br />。 - raoulinski
3
这将返回< br> 和 <div>。 - Nishad Up
显示剩余12条评论

69
将样式display:inline-block;添加到contenteditable,它将不会自动生成divpspan标签在Chrome浏览器中。

12
这是一个很棒的解决方案。*[contenteditable="true"]{display: inline-block;} - ericjbasti
1
这解决了我过去六个月一直试图修复的换行问题!谢谢@le-tung-anh - Jay Jee
15
不再适用于Chrome版本63.0.3239.84。 - Mikaël Mayer
1
当元素是网格子元素时,这种方法不起作用。inline-block 不会有任何效果,按 Enter 键仍然会插入 <div> 元素。 - user670839
1
@Bob Stein - 想在这里补充一下 - 在 contenteditable div 周围添加一个包装器 div,并使用 inline-block 显示,可以使其在网格子元素内正常工作! https://dev59.com/Pr74oIgBc1ULPQZF9mqq - shaedopotato
显示剩余6条评论

53

你可以仅通过 CSS 修改来实现此操作:

div{
    background: skyblue;
    padding:10px;
    display: inline-block;
}

pre{
    white-space: pre-wrap;
    background: #EEE;
}

http://jsfiddle.net/ayiem999/HW43Q/


1
当我使用inline-block时,执行execCommand("insertHTML", xxx)插入任何内容时出现问题,在Chrome33中测试后会在插入的元素末尾添加一个"<br>"。 - Imskull
7
不错的发现。遗憾的是,这种方法无法用于 position: absolute 元素或直接位于 display: flex 父元素下的子元素。不过你可以在考虑到这一点的情况下通过一些技巧使其正常工作。只要确保它不是 flex 元素的直接子元素即可。如果需要使用 position: absolute,只需为其添加一个额外的父元素即可。 - Sceptic
@ReinoutvanKempen已经开始使用Flex三个月了。我猜这个hack不会起作用。 - kittu
这是一个非常非常棒的解决方案,它非常干净和清晰,不会插入任何东西,甚至包括 br。特别是这个解决方案没有使用 js。 - defend orca
2
哇...这解决了Chrome的问题,但会让Firefox在按回车键时开始添加div,而这不是默认行为。 - cbdeveloper
当元素是网格子元素时,这也不起作用。inline-block 不会有任何作用,按 Enter 键仍然会插入 <div> 元素。 - user670839

47

此功能在所有主要浏览器(Chrome,Firefox,Safari,Edge)中均可使用。

document.addEventListener('keydown', event => {
  if (event.key === 'Enter') {
    document.execCommand('insertLineBreak')
    event.preventDefault()
  }
})
<div class="element" contenteditable="true">Sample text</div>
<p class="element" contenteditable="true">Sample text</p>

有一个不便之处是,在编辑完成后,元素内可能会包含一个结尾的<br>。但如果需要,您可以添加代码来修剪它。

查看此答案以删除结尾的<br>https://dev59.com/C13Ua4cB1Zd3GeqP8gjw#61237737


6
什么鬼?对我真正起作用的唯一解决方案怎么会在这一页下面这么远呢?非常感谢。 - user12861
你真是让我开心,这个问题花了好几个小时才解决 - 谢谢! - Alexander Kludt
1
execCommand已经被弃用!不再推荐使用此功能。虽然某些浏览器可能仍然支持它,但它可能已经从相关的Web标准中删除。 - ahsan ayub
1
这对于所有主要浏览器都有效,而且很可能会继续有效,因为没有人会从网络中删除东西。 - Obed Parlapiano
这对于不支持 contentEditable="plaintext-only" 的浏览器非常有效,并且几乎可以被所有浏览器使用。 - KeitelDOG
execCommand现已被弃用,不应再使用。请参考以下链接了解更多信息:https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand - undefined

25
document.execCommand('defaultParagraphSeparator', false, 'p');

它覆盖了默认行为,以使段落成为默认行为。

在 Chrome 上,默认的回车键行为是:

<div>
    <br>
</div>

有了那个命令,它就会变成这样

<p>
    <br>
</p>

现在它更线性了,只需要<br>就可以了。


当您按下回车键时,不管浏览器如何,您将会得到p和br而不是div和br。试一试。 - Ced
谢谢!解释总是有帮助的。 - jpaugh
@jpaugh 没问题,我进一步编辑了我的答案,以便更好地解释。 - Ced
这在火狐浏览器上不起作用(我只在谷歌浏览器和火狐浏览器上测试过)。 - medBouzid
这很好,但我真的希望你可以将其更改为只有 br 而没有额外的 p 标签。 - Max
显示剩余3条评论

25

试试这个:

$('div[contenteditable="true"]').keypress(function(event) {

    if (event.which != 13)
        return true;

    var docFragment = document.createDocumentFragment();

    //add a new line
    var newEle = document.createTextNode('\n');
    docFragment.appendChild(newEle);

    //add the br, or p, or something else
    newEle = document.createElement('br');
    docFragment.appendChild(newEle);

    //make the br replace selection
    var range = window.getSelection().getRangeAt(0);
    range.deleteContents();
    range.insertNode(docFragment);

    //create a new range
    range = document.createRange();
    range.setStartAfter(newEle);
    range.collapse(true);

    //make the cursor there
    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);

    return false;
});

http://jsfiddle.net/rooseve/jDvau/3/


好的,运行正常 :) 在我做出决定之前,我会等待更多关注,谢谢。 - iConnor
在 Firefox 上不能正常工作,它会添加一行额外的代码。 - Aman Gupta
这样做可以避免在按下回车键时触发“input”事件。一个简单的解决方案:添加类似于$(this).trigger('input')的内容。 - LarsW
“insertHTML”解决方案在存在嵌套的“contenteditable”元素时会出现一些奇怪的问题,您的解决方案规避了这个问题,但还有一些其他问题,我将其添加为一个新的可能的解决方案。 - Jelle De Loecker
不适用于Chrome浏览器。第一次在字符串末尾按Enter键时,它会添加一个空格。但第二次就可以正常工作了。 - user670839
1
我正在使用一个带有子div的contenteditable,并希望在该div中输入新行。代码运行良好,但如果我在行末输入,则无法正常工作。在行末,我必须输入两次才能换行。 - danish

19

W3C编辑草案中提供了一些关于将状态添加到可编辑内容( ContentEditable )的信息,为了防止在按下回车键时浏览器添加新元素,可以使用plaintext-only

<div contentEditable="plaintext-only"></div>

2
谢谢!这对我很有效。它不会添加 div 或 br。 - Serkan Sipahi
1
这就是我需要的! - stackoverflow
它仍处于非常早期的阶段。截至2021年12月,仅有57%的浏览器支持。 - Airy
1
很棒的解决方案,运行得非常好。但是Firefox和IE仍然不支持:https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable。我可以检查浏览器,在FF和IE中加载破解的解决方法。 - KeitelDOG
1
这是2022年常青浏览器的正确答案。 - Guerric P

13

使用shift+enter代替enter,只能插入一个单一的<br>标签,或者将文本包装在<p>标签中。


10
谢谢,这个信息很有用,但是如果你的客户正在写书,那会很麻烦。 - iConnor
2
如果您正在粘贴内容,这并没有帮助。 - vsync

6

contenteditable 在按下 enter 键时的行为取决于浏览器,<div> 在 Webkit 浏览器(Chrome、Safari)和 IE 上发生。

我在几个月前也遇到了这个问题,我是这样解决的:

//I recommand you trigger this in case of focus on your contenteditable
if( navigator.userAgent.indexOf("msie") > 0 || navigator.userAgent.indexOf("webkit") > 0 ) {
    //Add <br> to the end of the field for chrome and safari to allow further insertion
    if(navigator.userAgent.indexOf("webkit") > 0)
    {
        if ( !this.lastChild || this.lastChild.nodeName.toLowerCase() != "br" ) {
            $(this).html( $(this).html()+'<br />' );
        }
    }

    $(this).keypress( function(e) {
        if( ( e.keyCode || e.witch ) == 13 ) {
            e.preventDefault();

            if( navigator.userAgent.indexOf("msie") > 0 ) {
                insertHtml('<br />');
            }
            else {
              var selection = window.getSelection(),
              range = selection.getRangeAt(0),
              br = document.createElement('br');

              range.deleteContents();
              range.insertNode(br);
              range.setStartAfter(br);
              range.setEndAfter(br);
              range.collapse(false);

              selection.removeAllRanges();
              selection.addRange(range);
            }
        }
    });
}

我希望这篇文章能够帮助到你,如果我的英语不够清晰,对此我感到抱歉。

编辑:移除了jQuery函数jQuery.browser的修正。


只是想让你知道,jQuery.browser 不再是 jQuery 的一部分。 - iConnor
你是对的,我应该提到这一点并使用类似 navigator.userAgent.indexOf("msie") > 0navigator.userAgent.indexOf("webkit") > 0 这样的东西。 - Elie
1
需要将 if( ( e.keyCode || e.witch ) == 13 ) { ... } 改为 if (e.keyCode === 13 || e.which === 13) { ... } - Sebastian Sandqvist

6
你可以为每一行使用单独的<p>标签,而不是使用<br>标签,这样可以获得更好的浏览器兼容性。
要实现这个功能,可以在可编辑的
标签中放置一个带有默认文本的<p>标签。
例如,不要写成:
<div contenteditable></div>

使用:

<div contenteditable>
   <p>Replace this text with something awesome!</p>
</div>

jsfiddle

在Chrome,Firefox和Edge中测试,第二个在每个浏览器中都可以正常工作。

然而,第一个在Chrome中创建div,在Firefox中创建换行符,在Edge中创建div并且光标被放回到当前div的开头而不是移动到下一个div。

在Chrome,Firefox和Edge中测试过。


这实际上不是一个坏的解决方案。 - AaronHS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接