强制IE中的contentEditable元素在按下Enter键时创建换行符,而不破坏撤消功能。

29

在Internet Explorer浏览器中,使用contentEditable DIV功能时,每次按下Enter键都会创建一个新段落(<p></p>),而Firefox则会创建一个<br/>标签。

正如这里所讨论的,可以通过JavaScript拦截Enter键按下事件,使用range.pasteHTML来创建一个<br/>标签。但是这样做会破坏Undo菜单;一旦按下Enter键,您将无法撤消过去的操作点。

我如何强制contentEditable元素在按Enter键时创建单行换行符,而不会破坏Undo功能?


好的观点 - 即使是可用的所见即所得编辑器也可以帮助解决这个问题,因为它们实现了自己的撤销系统。 - Pekka
7个回答

17

虽然不是一个确切的解决方案,但这是另一个解决方法。如果您使用以下标记:

<div contenteditable="true">
<div>
content
</div>
<div>

然后按下 enter 将会创建新的 div 标签而不是 p 标签。


3
有趣!我在其他地方读过这个技术,但出于某种原因,在以前我无法使技术工作。我刚刚在一个简单的HTML页面中尝试了一下,它在IE6、IE8、FF36和GC4上都非常有效。然而,如果用户成功删除内部DIV,它会恢复到使用<p>换行的状态。 - Dan Fabulich
1
真的,我不知道你怎么能避免这个问题。最初,“contenteditable”是微软专有属性,他们以任何他们想要的方式实现它,其中一些是有意义的,但大多数都有太多的错误。FF的实现不那么有bug,但做了一些非标准的事情(输入应该创建新段落)。希望IE9重新考虑这个功能。为了在它们之间实现任何一致性,你真的必须考虑JavaScript。 - Joel
检查剃刀的答案,而不是EUW! - Mr W
我还发现,如果你给内部DIV添加一个类,那么IE会将相同的类添加到创建的DIV中。这可能是一个有用的技巧,可以选择性地替换或过滤这些标签。无论如何,这是一个不错的简单解决方案,对我很有效。 - Yogi
我无法确认这是否适用于p标签(它从未创建过p标签,而是div),但将内容包装在额外的外部标记中的解决方案似乎是一个很好的解决方法,以避免换行符创建布局破坏元素,这些元素您无法控制。 - kontur

5

在输入换行时按住Shift键,而不是在输入整段文字时按住Shift键。默认情况下,在Firefox中,对于包含一个段落的contenteditable区域,这种行为是相同的。

<div contenteditable="true">
    <p>If you are typing here</p>
<div>

这是一个很好的观点,但我不能期望我的用户始终记得这样做。 - Dan Fabulich
2
如果使用Word Perfect、Open Office甚至是Microsoft Word,人们可能会知道这个约定,因为它们都表现得一样。附带编辑器的说明即可。推广这个约定而不是试图打破它。 - Fenton
我完全同意 :) 这是一个技巧,一旦用户学会了它,就很少会忘记。它与在电子表格中使用 Ctrl-Click 选择单独行的空间相同。 - Brendon Muir

4

在it技术相关中,使用<BR>代替<P>标签(在IE9中测试过。在旧版本中,<BR>后可能需要nbsp;,如(pasteHTML("<br>&nbsp;"))

将其用于keydown事件:

if (e.keyCode == 13) {
 var range = document.selection.createRange();
 range.pasteHTML("<br> ");
 range.moveStart("character", 0);
 range.moveEnd("character", -1);
 range.select();
 return false;
}

这是只针对IE的解决方案吗?其他浏览器呢? - Timo Kähkönen
请记住 - 13键可以用于列表项插入。 - Alex from Jitbit

4

假设您要将可编辑的内容发布到服务器。因此,在用户编辑时,您不应该真正关心是否有段落或换行符标记,因为您可以在提交之前(或者如果您喜欢,在服务器上)解析HTML并用换行符替换段落的实例。这使得UNDO队列对用户保持有效,但是让您的HTML尽可能干净。

我进一步假设您将确切地知道哪些DIV元素将成为contentEditable。在提交表单之前,您可以将每个可编辑的div运行通过以下函数:

function cleanContentEditableDiv(div) {
  var htmlString = div.innerHTML;
  htmlString     = htmlString.replace(/<\/p>/gim,"<br/>");
  htmlString     = htmlString.replace(/<p>/gim,"");
  return htmlString;
}

你需要在循环中调用这个函数(遍历所有可编辑的DIV,使用一个名为ceDivs的数组),像这样:

ceDivs[i].contentEditable = false; // to make sure IE doesn't try to coerce the contents again

然后:

ceDivs[i].innerHTML = cleanContentEditableDiv(ceDivs[i]);

如果你不想在提交时进行操作的话,对此进行改进(特别是这个情况),可能是在任何其他时间清理每个这样的div的内容(例如onblur),并将每个ceDiv的内容分配给它自己的隐藏表单元素以供稍后提交。结合上面提到的CSS建议使用,这可能适合你。它没有严格保留你的要求(即,它没有让Javascript使IE的行为与其编码下的行为有所不同),但实际效果相同。用户得到了他们想要的,你也得到了你想要的。

在此期间,您还可以清理IE中的空格字符串。如果用户键入多个空格(如某些人在句号后仍然这样做),则IE插入的不是[space][space],而是[space]&nbsp;,一个初始空格字符(String.fromCharCode(32))加上与空格运行中剩余数量相同的&nbsp;实体。


我正在开发一个客户端编辑器。复制和粘贴到可编辑元素中以及从其中粘贴出来非常重要,因此保留换行符确实很重要。 - Dan Fabulich
Robusto提出了一个非常好的观点:清理HTML很重要。Contenteditable生成的一些HTML可能是你见过的最糟糕的。在任何情况下,在服务器端对HTML进行净化都是个好主意。就使用HTML作为客户端标记而言,这就是极限了。我们放弃了支持那些想从Word复制粘贴的客户端,因为最终没有人会感到更快乐的。:$ - Martijn Laarman
我同意清理HTML是个好主意,但一旦我清理了HTML,你就不能再使用撤销菜单来撤销更改了!这正是我提问的重点。 - Dan Fabulich
丹,有两点需要注意:
  1. 在我建议的方案中,您的换行符号被保留的。只是在事后重新调整了一下。
  2. 如果您将清理工作推迟到表单提交之后,那么用户就会留下整个UNDO队列。在表单提交之后不应该期望保存它。
- Robusto
2
Robusto,我正在尝试在用户按下Enter键的那一刻生成单倍行距,而不会在用户每次按下Enter键时打破撤消。清理服务器端是一个好的实践,但与我的问题无关。 - Dan Fabulich

2

也许很难做到完美无误。但是,可以通过使用CSS来去除段落标签周围的默认边距,使<p>标签“看起来”像单行换行符:

p { margin: 0; }

这种方法存在一个缺点:如果用户需要在contentEditable元素中复制/粘贴文本,那么该文本在元素内部可能会呈现单倍行距,但在元素外部则是双倍行距。

更糟糕的是,在一些情况下,IE将自动检测到双倍换行符,并用<p>标签替换它们,这将破坏已粘贴文本的格式。


元信息:通常我不愿意回答自己的悬赏问题,但已经过去四天了,没有任何回应;我不想看到这些点数消失在虚空中... - Dan Fabulich
在我看来,最好的解决方案。由于IE11不再在用户代理中发送“msie”,我们必须为其编写一个全新的用例,并使用一堆全新的黑客技巧...该死 - 让我们做边距的事情吧。 - Alex from Jitbit

1

IE在按下 Enter 键时将文本节点绑定到 <p> 标签上。要实现换行,请使用 Shift+Enter。Firefox在按下 Enter 键时添加一个 <br> 标签。要使这种行为在两个浏览器中通用,您可以执行以下操作:

假设有以下标记:

<div id="editableDiv" contentEditable="true"></div>

Javascript [假设下面的代码在函数闭包中,以便不会用全局变量使页面臃肿]:

 (function () {
     //Initialize _shiftKeyPressed to false
     var _shiftKeyPressed = false;
     $('#editableDiv').live('keydown', function (e) {
         if (e.keyCode == 16) {
             // SHIFT key is pressed
             _shiftKeyPressed = true;
         }
     }).live('keyup', function (e) {
         if (e.keyCode == 16) {
             // SHIFT key is release
             _shiftKeyPressed = false;
         }
     }).live('keypress', function (e) {
         if (e.keyCode == 13 && !_shiftKeyPressed && !jQuery.browser.msie) {
             // Insert a PARAGRAPH in Firefox instead of a BR
             // Ignores SHIFT + ENTER
             document.execCommand("insertParagraph", false, true);
         }
     })
 })();

注意:此脚本是在 jQuery < 1.9 版本中使用已弃用的 $.browser


0
考虑在使用Internet Explorer时使用<span contenteditable="true"></span>。您可能不想在Chrome中这样做,因为焦点看起来很奇怪。您可能需要实现自己的onfocusonblur代码。

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