bisgardo
bisgardo

Reputation: 4600

Refocus CKEditor at correct cursor position

I am building a CMS system, where I use jQuery and CKEditor for editing content inline. When an editor is blurred, the user is requested to confirm that he/she wants to discard the edits. If 'no' is chosen, the blur event should be cancelled and the editor retain focus without changing the cursor position. Since it seems to me that this is not possible, I try to refocus the editor after the blur is done. The following code snippet is from the blur event handler:

var $this = $(this);
if (confirm('Discard your changes?')) {
    $this.html($this.data('previous'));
} else {
    // Optimally prevent the blur event, but alternatively
    // reintroduce focus at correct position...
    $this.data('editor').focus();
}

Note that the focus call is done on the editor instance contained in $this.data('editor'), but the result seems to be the same as if I do it directly on $this.

The problem with this solution is that although it reintroduces focus, the cursor is now positioned at the beginning of the text, which is very unintuitive and annoying to the end user, who thought nothing changed. On the other hand, just giving up focus is not an option, since I would not like the user to be able to prevent resetting content and then go on to make other changes, thinking that the changes are persisted.

Therefore I would like a solution, where I can either prevent the blur altogether, or reintroduce the cursor at it's last position.

Upvotes: 2

Views: 9735

Answers (2)

oleq
oleq

Reputation: 15895

Native solutions (via window.getSelection()...) are unsafe since browsers implement this API in different ways or/and with bugs. The problem is that "standards" for selection system, ranges and contenteditable (in general) are very poor, blurry and neglected. CKEditor bypasses these issues (many kLOC) and keeps things portable and predictable thanks to its own API.

So don't reinvent the wheel and go ahead with this code (tested in latest Chrome and IE10):

var editor = CKEDITOR.instances.editor1;

editor.on( 'blur', function() {
    console.log( 'Editor blurred.' );

    var range = editor.getSelection().getRanges()[ 0 ]; // store the selection

    if ( confirm( 'Discard your changes?' ) ) {
        console.log( 'Changes discarded.' );
    } else {
        editor.focus(); // focus the instance again
        range.select(); // restore previous selection
    }   
} );

If you mix this code with cached data check (editor.getData()), you can easily avoid confirm() when nothing has really changed:

var editor = CKEDITOR.instances.editor1, cached;

editor.on( 'blur', function() {
    console.log( 'Editor blurred.' );
    var range = editor.getSelection().getRanges()[ 0 ]

    if ( editor.getData() != cached && confirm( 'Discard your changes?' ) ) {
        console.log( 'Changes discarded.' );
    } else {
        editor.once( 'focus', function( event ) { 
           event.cancel();  // cancel the *very next* event, nothing will be cached
        }, null, null, 0 );

        editor.focus();
        range.select();
    }   
} );

editor.on( 'focus', function() {
    console.log( 'Cached editor contents.' );
    cached = editor.getData();
} );

Upvotes: 5

Joshua - Pendo
Joshua - Pendo

Reputation: 4371

I'm not sure if it works with CKEditor, but with a textarea for example you can get the current cursor position by using:

var cursor = window.getSelection().getRangeAt(0).startOffset;

In this questions there's a jQuery function: Cursor position in a textarea (character index, not x/y coordinates)

Setting the cursor on the right spot would be the same as selection a specific part of the text, more info here: Highlight text range using JavaScript

I guess it needs a little bit of thinking since CKEditor most likely will replace the textarea.

Upvotes: 0

Related Questions