处理contentEditable DIV中的换行问题

74

我在SAFARI/CHROME上使用contenteditable时遇到了换行问题。当我在一个可编辑的<div>中按下“回车”键时,它们会创建一个新的<div>而不是像Firefox一样创建一个<br>标签:

<div>Something</div>
<div>Something</div>

看起来像这样(在contentEditable的DIV中):

Something
Something

但是在进行过净化处理(移除了 <div>)之后,我得到的结果是这样的:

SomethingSomething

在Firefox中,contenteditable的作用是:

Something
<br>
Something

经过清洗后,它看起来仍然一样:

Something
Something

有没有办法在各种浏览器中“规范化”这个问题?

我在Make a <br> instead of <div></div> by pressing Enter on a contenteditable上找到了这段代码。

$(function(){

  $("#editable")

  // make sure br is always the lastChild of contenteditable
  .live("keyup mouseup", function(){
    if (!this.lastChild || this.lastChild.nodeName.toLowerCase() != "br") {
      this.appendChild(document.createChild("br"));
     }
  })

  // use br instead of div div
  .live("keypress", function(e){
    if (e.which == 13) {
      if (window.getSelection) {
        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);
        return false;
      }
    }
  });
});
这个方法是有效的,但在Safari和Chrome浏览器中需要按两次“Return”键才能换行... 有什么想法吗?
编辑:使用我找到的代码(见本问题底部)可以正常工作,除了一个“确保<br>元素始终是lastChild”的函数之外...有什么解决方法吗?
编辑2:我在控制台上得到了这个错误:Uncaught TypeError: Object #<HTMLDocument> has no method 'createChild' 编辑3:好的,我把document.createChild("br");改成了document.createElement("br");,我认为它在FF / Safari / Chrome中都可以工作...所有的回车都会返回<br>...
问题现在是当我在有序或无序列表中时,我需要在没有<br>的情况下换行...
编辑4:如果有人对最后一次编辑的解决方案感兴趣:Avoid createElement function if it's inside a <LI> element (contentEditable)

你的数据清洗方法是什么样子的? - MikeM
@mdmullinax 它只是删除了所有的<div>... 我正在使用 https://github.com/gbirke/Sanitize.js 问题在于,当我按回车键时,Safari/Chrome只会创建一个新的DIV,而不是像Firefox一样创建<BR>。 - Santiago
1
也许你可以使用$("div:empty").replaceWith("<br/>"); - Anders Lindén
你对于两个回车键的Bug找到了什么有用的东西吗?我也遇到了同样的问题,唯一能做的就是一个hack(添加一个空文本并选择它,这样如果你继续写入,它就会自动消失),我会在那个帖子上发布的。虽然不完美,但确实有效。 - Micaël Félix
Firefox现在采用了混合方法:它将<br>标签包裹在<div>标签内。这使得它的行为类似于Chrome,我猜是因为React完全删除了<div>标签,包括<br>标签。 - trysis
8个回答

32

这种技巧似乎避免了Chrome的一个bug,即第一次不显示BR(使用您提到的代码需要按两次Enter键)。

这不是一个完美的hack,但它可以解决问题:在BR后添加一个空格使其正确显示。 然而,您会发现仅添加一个空格" "将不会改变任何内容,它与其他字母一起工作。浏览器不会显示它,可能是因为它像是HTML页面中的一个空格,它根本没有意义。为了摆脱这个bug,我使用jQuery创建了一个包含&nbsp;标记的div,并使用text()属性将其放入文本节点中,否则它就无法工作。

$editables = $('[contenteditable=true]');

$editables.filter("p,span").on('keypress',function(e){
 if(e.keyCode==13){ //enter && shift

  e.preventDefault(); //Prevent default browser behavior
  if (window.getSelection) {
      var selection = window.getSelection(),
          range = selection.getRangeAt(0),
          br = document.createElement("br"),
          textNode = document.createTextNode("\u00a0"); //Passing " " directly will not end up being shown correctly
      range.deleteContents();//required or not?
      range.insertNode(br);
      range.collapse(false);
      range.insertNode(textNode);
      range.selectNodeContents(textNode);

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

   }
});

1
在selection.removeAllRanges()之前添加range.deleteContents();,以消除那些讨厌的 ,同时保持一切正常工作^^ - seahorsepip
1
在“return false”之前添加document.execCommand('delete')就可以解决问题,而且不会显示选定的空格选择。 - Priyesh Diukar

21

您可以使用以下命令

document.execCommand('insertHTML',false,'<br>');

此命令将防止默认行为并添加您希望的标记。 就这样,只需要1行代码。

甚至更简单的方法。

仅使用CSS。

对于可编辑元素,请应用display:inline-block规则, 这将仅添加br标记。


2
这个 CSS 方法很棒!干杯 @Karb - nevada_scout
这两个解决方案都对我无效。尝试使用"keypress"、"keydown"和"keyup",以及同时使用这两个解决方案。但是Firefox仍然会创建DIVS :/ - Ann MB
请注意,如果您只是简单地添加“contenteditable=true”,那么浏览器的行为将不会完全相同,因为最终用户可见的行为尚未指定,并且每个浏览器供应商都有不同的想法。您必须使用更多或更少的完整JS实现来使浏览器发出您想要的输出。一个常见的选择是使用TinyMCE,它现在已经获得了MIT许可证,因此对于所有人来说,使用它(与开源插件一起)是免费的。 - Mikko Rantalainen

16

监听按键似乎需要很多工作。这种方法是否可行:

var html = $('.content').html().replace(/<div>/gi,'<br>').replace(/<\/div>/gi,'');

JSFiddle演示在这里

另外,看起来sanitize.js允许自定义配置。你尝试过将div作为允许的元素添加吗?

允许HTML/CSS是危险的,请确保你格外谨慎。

编辑:移除了服务器端注释。清理发生在使用时-如果客户端想要在本地绕过此问题,那么他们可以自行承担风险。感谢Vincent McNabb指出这一点。


3
看起来这种清洗是为了显示目的而不是安全目的,因此在客户端上执行它是完全合理的。如果他们想要规避它,那么所有它会做的就是让事情看起来很丑。 - Vincent McNabb
我相信在使用时进行消毒处理是出于安全目的(例如,为HTML输出剥离不安全的令牌)。您提出了一个很好的观点,即如果客户端想要在本地绕过此操作,可以自行决定承担风险。 - Kurt
你是否正在尝试使用正则表达式解析Html?在这样做之前,请阅读此处的建议:https://dev59.com/X3I-5IYBdhLWcg3wq6do#1732454 - Simon H

3
Webkit不会渲染容器的最后一个(非空)子元素是br的情况。对于Safari浏览器,插入换行符的快捷键是Ctrl + Return。如果你仔细观察,你会发现Safari实际上插入了两个br,但只渲染了一个;然而,如果有一些尾随文本,它将插入单个br,或者在输入一些内容后消除双重br(如果有的话)。
显然,Webkit的解决方案是像他们自己做的那样插入双重br,然后让它们处理其余部分。

2

您可以使用:

document.execCommand("defaultParagraphSeparator", false, "br");

完整的参考资料在这里。最初的回答。

1
你可能想要查看CSS的white-space属性。将样式设置为white-space: pre-wrap可以摆脱所有的JavaScript,因为它改变了空格的行为,以显示而不是折叠。

并参见此答案:HTML - DIV内容可编辑的换行符?


无论设置什么空格,这个问题仍然存在,这与contentEditable有关。 - rtaft

0

使用情况通常发生在您填充内容可编辑的div并将其重新发送到服务器时。

例如:像在社交媒体网站上编辑帖子一样,为了避免这种情况,我为每个新行创建了新的div。

let conD =data.postDescription.split('\n');
let conH = "";
conD.forEach((d)=>{
        conH +=`<div>${d}</div>`;
       });
$("#contentbox").html(conH);


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