Make the Tab key Insert a tab character in a contentEditable div and not blur

Viewed 21306

I'm building a simple text editor by setting contentEditable=true on a div (anyway i think that textarea behaves in the same way) and i have some problems with the tab key.

What i want is that when the user presses the tab key, the focus remain on the div and a tab character is added to the text.

I've solved the first part of the problem by calling preventDefault() on the event object on keydown and now the div doesn't lose the focus, but i don't know why i can't insert the character.

The entity code for the tab char is 	 but if i try to add this string to the innerHTML Firefox replace it with a simple space (not   just a white space). I also tried with \t but the result is the same.

So my question is, how can I insert a tab character in the text?

7 Answers

tabs and spaces are collapsed in html to one space unless in a <pre> tag or has white-space: pre in its style

I use this solution:

$('[contenteditable]').on('keydown', function(e){
    if(e.keyCode == 9){
        e.preventDefault();
        document.execCommand('insertHTML', false, '&#009');
    }
}).css('white-space', 'pre-wrap');

When working with editable code, a <pre> element does not need the last added css. keyup vs keydown and the position of e.preventDefault() may differ in browsers.

Note: I think binding the key-event or whatever from the whole document is to be avoided. I had strange problems with Safari / iOS desktop browsers with contenteditable elements and bubbling events.

The reason why you only see a simple space is because sequences of whitespace characters (except non-breaking spaces) are treated as a single space character inside divs. If you want the tab character to display in the way you expect you'll need to put it inside something like a <pre> element that renders whitespace as it is in the source HTML, or inside an element with white-space: pre set via CSS.

Below is a working example which prohibits the loss of focus when clicking Tab while the contenteditable already has focus, and instead inserts a tab character.

var elm = document.querySelector('[contenteditable]')

// when the contenteditable gets focused, start listening to key presses
elm.addEventListener('focus', onFocus)

// when the contenteditable looses focus, remove "keydown" event listener
elm.addEventListener('blur', onFocus)

function onFocus(){
  window.addEventListener('keydown', onKeyDown)
}

function onBlur(){
  window.removeEventListener('keydown', onKeyDown)
}

function onKeyDown(e) {
   if (e.keyCode != 9) return // tab key
    
    e.preventDefault();  // prevent default behaviour, which is "blur"

    var sel = document.getSelection();
    var range = sel.getRangeAt(0);

    var tabNodeValue = '\u0009' // with 4 spaces: Array(4).join('\u00a0')
    var tabNode = document.createTextNode(tabNodeValue);

    range.insertNode(tabNode);

    range.setStartAfter(tabNode);
    range.setEndAfter(tabNode); 
}
[contenteditable]{
  min-height: 150px;
  border: 1px solid black;
  padding: 5px;
  white-space: pre;  /* <-- needed in order to render "tab" unicode */
}
<div contenteditable></div>

Related