Search code examples
javascripteventsckeditormicrosoft-edgeonblur

How can I wait for CKEditor to blur before performing other actions?


I am trying to solve an issue regarding the ordering of events when using a CKEditor.

It seems that CKEditor intentionally waits a certain length of time (200ms) before raising a blur event. My understanding is that this is to prevent an erroneous blur when the user clicks from the edit box to the toolbar.

However, the result of this is that, in certain browsers, if a user clicks an element with a click event listener immediately after using the CKEditor, the click event fires before the blur event, and this is a problem for me because I need to handle the events in the order they actually took place (from the perspective of the user).

This behavior can be observed below in IE or Edge by entering some text in the CKEditor and clicking either the button or the checkbox without clicking out or tabbing out of the CKEditor. On my machine, the console shows a gap of about 10-20 ms between the two events firing, and sometimes the blur event even does fire first.

function timeStamp() { return new Date() % 1000000; }

CKEDITOR.disableAutoInline = true;
CKEDITOR.inline('myCkEditorBox', {
  on: {
    blur: function() {
      console.log(timeStamp(), 'CKEDITOR blur.');
    }
  }
});

document.getElementById('myButton').addEventListener('click', function() {
  console.log(timeStamp(), 'Button clicked.');
});
document.getElementById('myCheckbox').addEventListener('click', function() {
  console.log(timeStamp(), 'Checkbox clicked.');
});
#myCkEditorBox {
  border: 1px solid black;
  width: 400px;
  min-height: 200px;
  margin-top: 5em;
}
<script src="https://cdn.ckeditor.com/4.7.3/standard/ckeditor.js"></script>
<input type="button" value="Do something" id="myButton" />
<input type="checkbox" id="myCheckbox" />
<div id="myCkEditorBox" contenteditable="true"></div>

What I have tried:

  1. I have tried the following code from a forum post, which basically disables the blur delay completely:
CKEDITOR.focusManager.prototype.orig_blur = CKEDITOR.focusManager.prototype.blur;
CKEDITOR.focusManager.prototype.blur = function() { 
    CKEDITOR.focusManager.prototype.orig_blur.call(this,true); 
};

This does get the blur event to fire right away, but it causes problems with the editor's toolbar, causing dropdowns in the toolbar to not expand, or causing styles applied via the toolbar to flicker and then go away.

  1. I have tried reducing the blur delay to 10ms with the following:
CKEDITOR.focusManager._.blurDelay = 10;

This also gets the events to fire in the desired order, but it seems like it's a bad idea to tamper with CKEditor's internal settings, which are presumably given certain values for a reason (this is true of the other approach I tried too).

So I'm at a bit of a loss regarding what to do. If I knew that a blur event was on the way from a particular control, I could probably find a way to wait for it to finish before carrying out actions from other events. My page could have multiple CKEditor controls on it at a time, so I don't think that proactively firing blur events on all of them and waiting for all of them to finish before carrying out the actions from other events would be a good idea.

So what can I do to ensure that I handle CKEditor blur events before click events, even if they actually fire in the opposite order?


Solution

  • The most reliable solution seems to be checking if editor is blurred when input is clicked:

    var editor = CKEDITOR.inline( 'myCkEditorBox' );
    
    document.getElementById( 'myButton' ).addEventListener( 'click', function() {
      if ( editor.focusManager.hasFocus ) {
        editor.once( 'blur', function() {
          myButtonClickHandler();
        } );
      } else {
        myButtonClickHandler();
      }
    } );
    
    function myButtonClickHandler() {
      // Code which should be executed on 'myButton' click, but only after editor is blurred.
    }
    

    If editor is blurred just perform the regular flow of button click handler. If editor is still focused, just wait for the blur event and then perform regular flow of button click handler.