29

On Internet Explorer, a contentEditable DIV creates a new paragraph (<p></p>) each time you press Enter whereas Firefox creates a <br/> tag.

As discussed here, it is possible to use JavaScript to intercept the Enter keyPress and use range.pasteHTML to create a <br/> instead. But doing this breaks the Undo menu; once you hit Enter, you can no longer Undo past that point.

How can I force the contentEditable element to create single line breaks on Enter without breaking Undo?

Community
  • 1
  • 1
Dan Fabulich
  • 37,506
  • 41
  • 139
  • 175
  • Good point - and even the available WYSIWYG Editors can help with this, as they implement their own undo systems. – Pekka Feb 08 '10 at 10:41

7 Answers7

17

Not an exact solution, but another workaround. If you use the markup:

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

Then pressing enter will create new div tags instead of p tags.

Joel
  • 19,175
  • 2
  • 63
  • 83
  • 3
    Interesting! I'd read about that elsewhere, but for some reason I couldn't get the technique to work before. Tried it just now in a simple HTML page and it worked great in IE6 IE8 FF36 GC4. However, if the user manages to delete the inner DIV, it reverts back to `

    ` breaks.

    – Dan Fabulich Feb 16 '10 at 17:03
  • 1
    True, and I don't know how you can avoid that. Originally `contenteditable` was a Microsoft proprietary attribute that they implemented in whatever way they wanted, some of which makes sense but most of which has far too many bugs. The FF implementation is less buggy but does some non-standard things (enter really should create new paragraphs). Here's hoping that IE9 reconsiders this feature. To achieve any semblance of consistency between them you really have to consider javascript. – Joel Feb 16 '10 at 18:45
  • euw!check razors answer instead! – Mr W Nov 28 '13 at 09:31
  • I also found that if you add a class to the inner DIV then IE adds the same class to the created DIVs. That might be a useful trick for selectively replacing or filtering those tags. Anyway, this is a nice simple solution and it worked for me. – Yogi May 03 '14 at 23:04
  • I can't confirm this applying to p tags (it never created p tags, but divs) but the solution to wrap content in an additional outer tag seems to be a good work around to avoid line breaks creating layout breaking elements you have no control over. – kontur Feb 15 '19 at 08:15
5

Hold down shift when you press enter for breaks, don't hold it down for full paragraphs. This behaviour is the same by default in Firefox, given a contenteditable area containing a paragraph: i.e.

<div contenteditable="true">
    <p>If you are typing here</p>
<div>
Fenton
  • 241,084
  • 71
  • 387
  • 401
  • That's a good point, but I can't expect my users to remember to do that consistently. – Dan Fabulich Feb 16 '10 at 20:52
  • 2
    People may know the convention if they use Word Perfect, Open Office or even Microsoft Word as these all behave the same. A note to this effect to accompany the editor should suffice. Promote the convention rather than try to break it. – Fenton Feb 17 '10 at 08:26
  • I totally agree :) It's a trick that my users seldom forget once they learn it. It's in the same space as Ctrl-Click to select individual lines in a spreadsheet. – Brendon Muir May 03 '10 at 03:50
4

Presumably you are going to post the editable content back to a server. Therefore, you shouldn't really care whether you have paragraph or break tags in place while the user is editing, because you can parse the HTML before submission (or on the server, if you like) and replace instances of paragraphs with breaks. This keeps the UNDO queue in operation for the user, but lets you have your HTML as clean as you want it.

I'll further presume that you're going to know exactly which DIV elements are going to be contentEditable. Before the form is submitted, you can run each contentEditable div through a function like this:

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

And you call this in a loop (which iterates through all contentEditable DIVs, using an array I will call ceDivs), like this:

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

and then:

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

An improvement on this (especially if you don't want to do this right at submit time), might be to clean the contents of each such div any other time you like (onblur, whatever) and assign the contents of each ceDiv to its own invisible form element for later submission. Used in combination with your own CSS suggestion above, this might work for you. It doesn't preserve your literal requirements to the letter (i.e., it doesn't get Javascript to make IE behave differently than it has been coded under the covers to behave) but for all practical purposes the effect is the same. The users get what they want and you get what you want.

While you're at it you may also want to clean space strings in IE as well. If the user types multiple spaces (as some still do after periods), what is inserted by IE is not [space][space] but [space]&nbsp;, an initial space character (String.fromCharCode(32)) plus as many &nbsp; entities as there are left in the space run.

Robusto
  • 31,447
  • 8
  • 56
  • 77
  • I'm working on a client-side editor. Copying and pasting into and out of the editable element is very important, so it *does* matter whether line breaks are preserved. – Dan Fabulich Feb 16 '10 at 16:56
  • Robusto makes a very good point on cleaning up the html. Contenteditable produces some of the worst html your eyes ever seen. Sanitizing the html on the server side in any case is a good idea. That is as far as using html for client side markup is ever a good idea. We gave up on supporting clients wanting to copy paste from word in the long run noone gets any happier :$ – Martijn Laarman Feb 16 '10 at 17:18
  • I agree that cleaning the HTML is a good idea, but once I clean the HTML, you can no longer use the Undo menu to undo the changes! That's the whole point of my question. – Dan Fabulich Feb 16 '10 at 20:50
  • Dan, two points: 1. In the scheme I suggested, your line breaks *are* preserved. They're just rejiggered after the fact. 2. If you leave the cleaning until the form is submitted, you leave the user with the entire UNDO queue. There should be no expectation of having that saved *after* form submission. – Robusto Feb 16 '10 at 21:06
  • 2
    Robusto, I'm trying to generate single-spaced lines at the very moment the user hits the Enter key, without breaking Undo every time the user hits Enter. Cleaning server-side is good practice, but irrelevant to my problem. – Dan Fabulich Feb 17 '10 at 06:43
4

Use <BR> instead of <P> (Tested in IE9. Maybe nbsp; is needed in older versions after <BR> (pasteHTML("<br>&nbsp;")) )

Use it on keydown event:

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

It may be impossible to get this right. However, it is possible to make the <p> tags look like single line breaks, by removing the built-in margin around paragraph tags, using CSS:

p { margin: 0; }

There is a drawback to this approach: if the user needs to copy/paste text in the contentEditable element, the text may appear to be single-spaced inside the element but double-spaced outside the element.

Worse, in at least some cases, IE will automatically detect double-line breaks during paste, replacing them with <p> tags; this will mess up the formatting of the pasted text.

Dan Fabulich
  • 37,506
  • 41
  • 139
  • 175
  • Meta aside: I'm normally loathe to post an answer to my own bounty, but it's been four days and no response; I'd hate to see those points disappear into the ether... – Dan Fabulich Feb 14 '10 at 21:42
  • Best solution IMO. Since IE11 does not send "msie" in user-agent any more, we have to write a whole new case for it, with a whole new bunch of hacks... Darn it - lets do the margin-thing. – Alex from Jitbit Apr 24 '15 at 20:10
1

IE binds the text node with a <p> tag on Enter key press. To achieve a line break Shift+Enter. Firefox adds a <br> tag on Enter key press. To make the behavior common across both the browsers, you can do the following:

Assume the following markup:

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

Javascript [assume the below code is in a function closure so that you don't bloat the page with globals]:

 (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);
         }
     })
 })();

Note: this script is prior to jQuery < 1.9 for the use of the deprecated $.browser

Daniel Beck
  • 20,653
  • 5
  • 38
  • 53
nairvijays
  • 181
  • 8
0

Consider using <span contenteditable="true"></span> when using Internet Explorer. You may not want to do this with Chrome because the focus looks funny. You will probably want to implement your own onfocus and onblur code.

700 Software
  • 85,281
  • 83
  • 234
  • 341