Force IE contentEditable element to create line breaks on Enter key, without breaking Undo
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?
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.
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>
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] , an initial space character (String.fromCharCode(32)) plus as many entities as there are left in the space run.
Use <BR>
instead of <P>
(Tested in IE9. Maybe nbsp;
is needed in older versions after <BR>
(pasteHTML("<br> "))
)
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;
}
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.
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
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.
精彩评论