setting loses caret position on <pre> element on pressing TAB key

1k views Asked by At

I have pre with contentEditable="true" on my webpage and I am trying to make it append "\t" when I press <TAB>. I found some other plugins for that but they were working only on <textarea>.

So, the problem is that when I append text to the <pre> through jQuery, it loses the caret position. I was sure it was losing focus but it's not. So $("pre").focus(), will do nothing.

I tried to blur it first but this is not practical cause the caret will return on different position depending on the browser. some help please... :P,

My code is here: http://jsfiddle.net/parisk/kPRpj/

4

There are 4 answers

1
James Khoury On

firstly Try

$("pre").keydown(function(e){
    if (e.keyCode==9) {
        e.preventDefault();
        $("pre").append("\t");   
    }
});

for inserting a tab. it will insert a tab after your cursor

then in chrome

var myselection = document.getSelection();
myselection.extend(jQuery("pre")[0].lastChild, jQuery("pre")[0].lastChild.length)
myselection.collapseToEnd();

in IE8

var myselection = document.selection;
myselection.extend(jQuery("pre")[0].lastChild, jQuery("pre")[0].lastChild.length)
myselection.collapseToEnd();

should get you caret at the end.

(all together)

$("pre").keydown(function(e){
    if (e.keyCode==9) {
        e.preventDefault();
        $("pre").append("\t"); 

        var myselection = null;
        if(document.getSelection)
        {
            myselection = document.getSelection();
        }
        else if (document.selection)
        {
            myselection = document.selection;
        }

        if(myselection != null)
        {
            myselection.extend(jQuery("pre")[0].lastChild, jQuery("pre")[0].lastChild.length)
            myselection.collapseToEnd();
         }
    }
});

EDIT it would also be safer to add in a test to see if the lastChild is null and other such. Also in the keydown function jQuery("pre") would be better to be jQuery(this) and put into a variable if you are using more than once. (which the example is but I'm too lazy)

0
s4y On

Try using document.execCommand instead. Here’s a demo. In short:

$("pre").keydown(function(e){
    if (e.keyCode==9) {
        e.preventDefault();
        document.execCommand('InsertHTML', false, '\t');
    }
});
0
Rikon On

To only your question, the jquery caret libraries are pretty good at encapsulating the quirks around the different browsers (namely IE): http://plugins.jquery.com/plugin-tags/caret

You could always just use this to get an index of all the tab positions and then clean them later...

0
Suzanne Soy On

James Khoury's answer always puts the caret at the end of the <pre>, which is not desirable when the user hits tab in the middle of the text.

Here is a modified version tha fixes this issue : http://jsfiddle.net/kPRpj/12/. It should be fairly trivial to make tab indent the line instead of adding a tab character at the caret position. To do that, you would replace

container.textContent = text.substring(0,start)+'\t'+text.substring(end);

with

var left = text.substring(0,start);
var lastLine = left.lastIndexOf("\n");
if (lastLine == -1) lastLine = 0;
container.textContent = left.substring(0,lastLine)+'\t'+text.substring(lastLine);

I've put a version that behaves this way on jsfiddle. However you might need to work around some inconsistencies in their management of '\n\r'. For example, I see that chromium sees each line as a separate text node, which is rather unexpected. I haven't tested it in other browsers.