iConnor
iConnor

Reputation: 20189

Prevent contenteditable adding <div> on ENTER - Chrome

I have a contenteditable element, and whenever I type some stuff and hit ENTER it creates a new <div> and places the new line text in there. I don't like this one little bit.

Is it possible to prevent this from happening or at least just replace it with a <br>?

Here is demo http://jsfiddle.net/jDvau/

Note: This is not an issue in firefox.

Upvotes: 190

Views: 132366

Answers (25)

lll
lll

Reputation: 476

There are different solutions on the internet and most of them handle keypress same way e.g execCommand, charCode, keyCode, which. Unfortunatelly these are deprecated and non-standard features use key feature instead of these.

Also do not use contentEditable="plaintext-only" try to handle enter keypress and insert br note to current selection.

Check my solution below it's working without any issue in Chrome, Firefox, Opera and Edge.

function handleEnterPress(e) {
  if (e.key.toLowerCase() === 'enter') {
    e.preventDefault();
    let selection = window.getSelection();
    let range = selection.getRangeAt(0);
    range.insertNode(document.createElement('br'));
    selection.collapseToEnd();
  }
  return false;
}

function handleEnterPress(e) {
  if (e.key.toLowerCase() === 'enter') {
    e.preventDefault();
    let selection = window.getSelection();
    let range = selection.getRangeAt(0);
    range.insertNode(document.createElement('br'));
    selection.collapseToEnd();
  }
  return false;
}
div[contenteditable="true"] {
  background-color: bisque;
  padding: 80px;
  border: 1px solid black;
}
<div contenteditable="true" onkeypress="handleEnterPress(event)">Lorem ipsum dolor sit amet, consectetur adipisicing elit.</div>

Upvotes: 1

Chetan Jain
Chetan Jain

Reputation: 241

we can clean html at end like so


function cleanHtml(innerHTML: string) {
  return innerHTML.replaceAll('<div><br></div>', '<br>')
}

in contentEditable do this

const { innerHTML, innerText } = contentEl
          console.log({ innerHTML, innerText, cleanHtml: cleanHtml(innerHTML) })

Upvotes: 1

Jelle De Loecker
Jelle De Loecker

Reputation: 21915

The inserHTML command solution does some weird things when you have nested contenteditable elements.

I took some ideas from multiple answers here, and this seems to suit my needs for now:

element.addEventListener('keydown', function onKeyDown(e) {

    // Only listen for plain returns, without any modifier keys
    if (e.which != 13 || e.shiftKey || e.ctrlKey || e.altKey) {
        return;
    }

    let doc_fragment = document.createDocumentFragment();

    // Create a new break element
    let new_ele = document.createElement('br');
    doc_fragment.appendChild(new_ele);

    // Get the current selection, and make sure the content is removed (if any)
    let range = window.getSelection().getRangeAt(0);
    range.deleteContents();

    // See if the selection container has any next siblings
    // If not: add another break, otherwise the cursor won't move
    if (!hasNextSibling(range.endContainer)) {
        let extra_break = document.createElement('br');
        doc_fragment.appendChild(extra_break);
    }

    range.insertNode(doc_fragment);

    //create a new range
    range = document.createRange();
    range.setStartAfter(new_ele);
    range.collapse(true);

    //make the caret there
    let sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);

    e.stopPropagation();
    e.preventDefault();

    return false;
});

// See if the given node has a next sibling.
// Either any element or a non-empty node
function hasNextSibling(node) {

    if (node.nextElementSibling) {
        return true;
    }

    while (node.nextSibling) {
        node = node.nextSibling;

        if (node.length > 0) {
            return true;
        }
    }

    return false;
}

Upvotes: 5

Hmerman6006
Hmerman6006

Reputation: 1913

I use hundreds of <td> elements to input calculated values and found Firefox was adding a <div></div><br /> on enter which is not cool. So I used this onkeyup:

$(document).on("keyup", '.first_td_col', function(e) {
    if (e.key === 'Enter' || e.keyCode === 13) {
       e.preventDefault();
       $(this).empty();

       return false;
    }
    // Do calculations if not enter
});

Of course this clears everything from the <td>, even the users' input. But with a message displaying No enter please., it solves unnecessary elements getting in the way of just doing the calculation.

Upvotes: 1

user670839
user670839

Reputation:

This works in all major browsers (Chrome, Firefox, Safari, Edge)

document.addEventListener('keydown', event => {
  if (event.key === 'Enter') {
    document.execCommand('insertLineBreak')
    event.preventDefault()
  }
})
<div class="element" contenteditable="true">Sample text</div>
<p class="element" contenteditable="true">Sample text</p>

There is one inconvenience. After you finish editing, the elements might contain an ending <br> inside. But you could add code to trim that down if you need to.

Check this answer to remove the trailing <br> https://stackoverflow.com/a/61237737/670839

Upvotes: 50

Ram G Athreya
Ram G Athreya

Reputation: 4952

Try this:

$('div[contenteditable]').keydown(function(e) {
    // trap the return key being pressed
    if (e.keyCode === 13) {
        // insert 2 br tags (if only one br tag is inserted the cursor won't go to the next line)
        document.execCommand('insertHTML', false, '<br/>');
        // prevent the default behaviour of return key pressed
        return false;
    }
});

Click here for demo

Upvotes: 183

StefansArya
StefansArya

Reputation: 2888

On the W3C Editor's Draft there are some information about adding states into ContentEditable, and to prevent the browser for adding new element when pressing enter you can use plaintext-only.

<div contentEditable="plaintext-only"></div>

Upvotes: 22

Gauss
Gauss

Reputation: 1148

I like to use Mousetrap for handlling hotkeys: https://craig.is/killing/mice

Then, I just intercept the enter event, executing a command insertLineBreak:

Mousetrap.bindGlobal('enter', (e)=>{
  window.document.execCommand('insertLineBreak', false, null);
  e.preventDefault();
});

All commands: https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand

It works using Chrome 75 and the following editable element:

<pre contenteditable="true"></pre>

It's also possible to use insertHTML:

window.document.execCommand('insertHTML', false, "\n");

Upvotes: 5

Josh Powlison
Josh Powlison

Reputation: 781

You can have separate <p> tags for each line rather than using <br> tags and gain greater browser compatibility out of the box.

To do this, put a <p> tag with some default text inside of the contenteditable div.

For example, instead of:

<div contenteditable></div>

Use:

<div contenteditable>
   <p>Replace this text with something awesome!</p>
</div>

jsfiddle

Tested in Chrome, Firefox, and Edge, and the second works the same in each.

The first, however, creates divs in Chrome, creates line breaks in Firefox, and in Edge creates divs and the cursor is put back at the beginning of the current div instead of moving into the next one.

Tested in Chrome, Firefox, and Edge.

Upvotes: 6

Ced
Ced

Reputation: 17327

document.execCommand('defaultParagraphSeparator', false, 'p');

It overrides the default behavior to have paragraph instead.

On chrome the default behavior on enter is:

<div>
    <br>
</div>

With that command it is gonna be

<p>
    <br>
</p>

Now that it's more linear across it's easy to have only <br> would you need to.

Upvotes: 26

DrShyam Babu Gupta
DrShyam Babu Gupta

Reputation: 9

Prevent New Div creation in content editable Div on each enter key: Solution That I have find is very simple:

var newdiv = document.createElement("div"); 
newdiv.innerHTML = "Your content of div goes here";
myEditablediv.appendChild(newdiv);

This --- innerHTML content prevent New Div creation in content editable Div on each enter key.

Upvotes: 0

Omnicon
Omnicon

Reputation: 314

It is possible to prevent this behaviour entirely.

See this fiddle

$('<div class="temp-contenteditable-el" contenteditable="true"></div>').appendTo('body').focus().remove();

Upvotes: -1

airi
airi

Reputation: 673

You can do this with just a CSS change:

div{
    background: skyblue;
    padding:10px;
    display: inline-block;
}

pre{
    white-space: pre-wrap;
    background: #EEE;
}

http://jsfiddle.net/ayiem999/HW43Q/

Upvotes: 55

Le Tung Anh
Le Tung Anh

Reputation: 899

Add style display:inline-block; to contenteditable, it will not generate div, p and span automatically in Chrome.

Upvotes: 76

webprogrammer
webprogrammer

Reputation: 790

if (navigator.userAgent.toLowerCase().indexOf('msie') > -1) {
   var range = document.getSelection();
   range.pasteHTML(range.htmlText + '<br><br>');
}
else if(navigator.userAgent.toLocaleLowerCase().indexOf('trident') > -1)                           {
   var range = document.getSelection().getRangeAt(0); //get caret
   var nnode = document.createElement('br');
   var bnode = document.createTextNode('\u00A0'); //&nbsp;
   range.insertNode(nnode);
   this.appendChild(bnode);
   range.insertNode(nnode);                                
}
else
   document.execCommand('insertHTML', false, '<br><br>')

Where this is the actual context which means document.getElementById('test');.

Upvotes: 1

nes
nes

Reputation: 1096

You can wrap your paragraphs with <p> tag for example it would apper on new line instead of div Example:
<div contenteditable="true"><p>Line</p></div>
After inserting new string:
<div contenteditable="true"><p>Line</p><p>New Line</p></div>

Upvotes: 3

MrAJ
MrAJ

Reputation: 95

Another way to do it

$('button').click(function(){
    $('pre').text($('div')[0].outerHTML)
});

$("#content-edit").keydown(function(e) {
    if(e.which == 13) {
       $(this).find("div").prepend('<br />').contents().unwrap();
      }
});

http://jsfiddle.net/jDvau/11/

Upvotes: 0

Mehdi
Mehdi

Reputation: 4318

This is browser directed HTML5 editor. You can wrap your text with <p>...</p>, then whenever you press ENTER you get <p></p>. Also, the editor works this way that whenever you press SHIFT+ENTER it inserts <br />.

<div contenteditable="true"><p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolor veniam asperiores laudantium repudiandae doloremque sed perferendis obcaecati delectus autem perspiciatis aut excepturi et nesciunt error ad incidunt impedit quia dolores rerum animi provident dolore corporis libero sunt enim. Ad magnam omnis quidem qui voluptas ut minima similique obcaecati doloremque atque!
<br /><br />
Type some stuff, hit ENTER a few times, then press the button.
</p>
</div>

Check this: http://jsfiddle.net/ZQztJ/

Upvotes: 2

L105
L105

Reputation: 5419

First we need to capture every key user enter to see if enter is pressed then we prevent the <div> creation and we create our own <br> tag.

There's one problem, when we create it our cursor stay at the same position so we use Selection API to place our cursor at the end.

Don't forget to add a <br> tag at the end of your text because if you don't the first enter won't do a new line.

$('div[contenteditable]').on('keydown', function(e) {
    var key = e.keyCode,
        el  = $(this)[0];
    // If Enter    
    if (key === 13) {
        e.preventDefault(); // Prevent the <div /> creation.
        $(this).append('<br>'); // Add the <br at the end

        // Place selection at the end 
        // http://stackoverflow.com/questions/4233265/contenteditable-set-caret-at-the-end-of-the-text-cross-browser
        if (typeof window.getSelection != "undefined"
            && typeof document.createRange != "undefined") {
            var range = document.createRange();
            range.selectNodeContents(el);
            range.collapse(false);
            var sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
        } else if (typeof document.body.createTextRange != "undefined") {
            var textRange = document.body.createTextRange();
            textRange.moveToElementText(el);
            textRange.collapse(false);
            textRange.select();
        }
    }
});

Fiddle

Upvotes: 2

cbayram
cbayram

Reputation: 2259

I'd use styling (Css) to fix the issue.

div[contenteditable=true] > div {
  padding: 0;
} 

Firefox indeed adds a the block element break
, whereas Chrome wraps each section in a tag. You css gives divs a padding of 10px along with the background color.

div{
  background: skyblue;
  padding:10px;
}

Alternatively, you can replicate the same desired effect in jQuery:

var style = $('<style>p[contenteditable=true] > div { padding: 0;}</style>');
$('html > head').append(style);

Here's a fork of your fiddle http://jsfiddle.net/R4Jdz/7/

Upvotes: 4

Elie
Elie

Reputation: 92

The way that contenteditable behaves when you press enter depends on browsers, the <div> happens on webkit (chrome, safari) and IE.

I struggled with this few month ago and I corrected it this way :

//I recommand you trigger this in case of focus on your contenteditable
if( navigator.userAgent.indexOf("msie") > 0 || navigator.userAgent.indexOf("webkit") > 0 ) {
    //Add <br> to the end of the field for chrome and safari to allow further insertion
    if(navigator.userAgent.indexOf("webkit") > 0)
    {
        if ( !this.lastChild || this.lastChild.nodeName.toLowerCase() != "br" ) {
            $(this).html( $(this).html()+'<br />' );
        }
    }

    $(this).keypress( function(e) {
        if( ( e.keyCode || e.witch ) == 13 ) {
            e.preventDefault();

            if( navigator.userAgent.indexOf("msie") > 0 ) {
                insertHtml('<br />');
            }
            else {
              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);
            }
        }
    });
}

I hope it will help, and sorry for my english if it's not as clear as needed.

EDIT : Correction of removed jQuery function jQuery.browser

Upvotes: 6

Blake Plumb
Blake Plumb

Reputation: 7199

Use shift+enter instead of enter to just put a single <br> tag in or wrap your text in <p> tags.

Upvotes: 13

Dan Rey Oquindo
Dan Rey Oquindo

Reputation: 276

Try using ckeditor. It also provide formatting like this one in SOF.

https://github.com/galetahub/ckeditor

Upvotes: 2

Math chiller
Math chiller

Reputation: 4167

add a prevent default to the div

document.body.div.onkeydown = function(e) {
    if ( e.keycode == 13 ){
        e.preventDefault();
            //add a <br>
        div = document.getElementById("myDiv");
        div.innerHTML += "<br>";
    }
}

Upvotes: 2

Andrew
Andrew

Reputation: 5340

Try this:

$('div[contenteditable="true"]').keypress(function(event) {

    if (event.which != 13)
        return true;

    var docFragment = document.createDocumentFragment();

    //add a new line
    var newEle = document.createTextNode('\n');
    docFragment.appendChild(newEle);

    //add the br, or p, or something else
    newEle = document.createElement('br');
    docFragment.appendChild(newEle);

    //make the br replace selection
    var range = window.getSelection().getRangeAt(0);
    range.deleteContents();
    range.insertNode(docFragment);

    //create a new range
    range = document.createRange();
    range.setStartAfter(newEle);
    range.collapse(true);

    //make the cursor there
    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);

    return false;
});

http://jsfiddle.net/rooseve/jDvau/3/

Upvotes: 25

Related Questions