Dealing with line Breaks on contentEditable DIV

JqueryContenteditableSanitization

Jquery Problem Overview


I have a problem with contenteditable line breaks on SAFARI/CHROME. When I press "return" on a contentEditable <div>, instead of creating a <br> (like Firefox), they create a new <div>:

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

That looks like (on the contentEditable DIV):

Something
Something

But after sanitization (removing <div>), I get this:

SomethingSomething

In Firefox, the contenteditable is:

Something
<br>
Something

And that after sanitization looks the same:

Something
Something

Is there any solution to "normalize" this across browsers?

I've found this code on https://stackoverflow.com/questions/3080529/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;
      }
    }
  });
});

This works, but (in SAFARI and CHROME) I have to press two times the "return" key to get a new line...

Any idea?

Edit: With the code I found ( at the bottom of this question) is working fine except the function that "makes sure a <br> element is always the lastChild... Any idea on how to fix this?

Edit 2: I'm getting this error on the console: Uncaught TypeError: Object #<HTMLDocument> has no method 'createChild'

Edit 3: Ok, I changed the document.createChild("br"); to document.createElement("br"); and I think I got it working in FF/Safari/Chrome... All return <br> for new lines...

The problem is now that when I'm inside an Ordered or Unordered List, I need to get a new line without <br>...

Edit 4: If anyone interested in the solution of the last edit: https://stackoverflow.com/questions/6024594/avoid-createelement-function-if-its-inside-a-li-element-contenteditable

Jquery Solutions


Solution 1 - Jquery

This technique appears to avoid the Chrome bug that does show BRs the first time (with the code you mentionned you need to push two times the Enter key).

It's not a perfect hack but it works: it adds a whitespace after your BR so it show properly. However, you will see that adding only a whitespace " " will NOT change anything, it works with other letters. The browser will not show it, probably because it is like a blank space inside an html page, it simply does not mean anything. To get rid of that bug I created a div with jQuery that includes a &nbsp; tag, and I take the text() property to put it in the textnode otherwise it doesn't work.

$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;
  }

   }
});

Solution 2 - Jquery

Listening to keystrokes seems like a lot of work. Would something as simple as this be feasible:

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

JSFiddle demo here.

Also, it looks like sanitize.js allows a custom configuration. Have you tried adding div as an allowed element?

Allowing HTML/CSS is dangerous, so ensure you are being extra cautious.

EDIT: Removed server side comments. Sanitising occurs at time of usage- if the client wants to bypass this locally it can be done so at their own risk. Thanks Vincent McNabb for pointing this out.

Solution 3 - Jquery

You can simply use following command document.execCommand('insertHTML',false,'<br>');

This command will prevent default behavior and add a tag you wish. That's all, 1 line of code

And even easier approach.

CSS ONLY.

To you contenteditable element apply display:inline-block rule and this will add brs only

Solution 4 - Jquery

Webkits won’t render a single br if it’s the last (non-empty) child of a container. For Safari, the native keystroke to insert a line break is Ctrl + Return. If you look closely, you notice that Safari actually inserts two brs while only rendering one; however, it will insert a single br if there is some trailing text, or will get rid of double brs once you enter some (if there were doubles).

Apparently, it seems a solution for Webkits is to insert double brs as they themselves do, and let them handle the rest.

Solution 5 - Jquery

You might want to check out the css white-space property. Setting the style to white-space: pre-wrap allows you to get rid of all that javascript, because it changes the behavior of whitespace to display instead of collapse.

And see this answer: https://stackoverflow.com/questions/19038070/html-newline-char-in-div-content-editable

Solution 6 - Jquery

You can use:

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

The complete reference is here

Solution 7 - Jquery

Solution 8 - Jquery

The use case generally occurs when you are prefilling the data in a content editable div & resending it to the server.

Eg. - Like editing a post on a social media website, to mitigate this I have created new div for each new line.

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSantiagoView Question on Stackoverflow
Solution 1 - JqueryMicaël FélixView Answer on Stackoverflow
Solution 2 - JqueryKurtView Answer on Stackoverflow
Solution 3 - JqueryKarbView Answer on Stackoverflow
Solution 4 - JqueryKlim LeeView Answer on Stackoverflow
Solution 5 - JqueryB TView Answer on Stackoverflow
Solution 6 - JquerySaeedView Answer on Stackoverflow
Solution 7 - Jqueryuser10View Answer on Stackoverflow
Solution 8 - JqueryMoonis AbidiView Answer on Stackoverflow