最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - setReadOnly causes error when called on instanceReady of CKEditor - Stack Overflow

programmeradmin5浏览0评论

I'm trying to set my CKEditor instance to be "readOnly" after the instance has fully loaded but I'm getting a Javascript error: Cannot call method 'setReadOnly' of null. When I dig into it, the error is ing from this line in the ckeditor.js, within the editor.setReadOnly method: this.editable().setReadOnly(a); That means that the editor exists, but the editable method/attribute (on the CKEditor instance) does not.

Below is my code, and I'll explain it a little. My app is a bination of GWT and Backbone. The CKEditor itself is created by the Backbone code but the parent element is in GWT so that's where I initiate the setEnabled action.

private native void setEnabledOnLoad(boolean enabled, String id) /*-{
  CKEDITOR.on("instanceReady", function(evt) {
    if(evt.editor.name === id) {
      Namespace.trigger(Namespace.Events.SET_ENABLED, enabled);
    }
  });
}-*/;

setEnabled: function(enabled) {
  this.editor.setReadOnly(!enabled);
  if(enabled){
    this.editor.focusManager.focus();
  } else {
    this.editor.focusManager.blur();
  }
}

The Backbone class has a listener for Namespace.Events.SET_ENABLED that triggers setEnabled.

Is there another CKEditor event that I should listen for? There doesn't appear to be an instanceReady event on editable. What am I missing?

EDIT
this.editor is created in the Backbone class render function like this:

this.editor = CKEDITOR.replace(this.$(this.id)[0], config);

The reason I don't add the instanceReady listener right after it's created is because the function setEnabledOnLoad is called in GWT before the instance has been fully initialized. This is a result of having the code in two places. GWT has said "ok, create the instance" but Backbone hasn't finished by the time GWT goes to the next line of code and wants to set it enabled/disabled.

I'm trying to set my CKEditor instance to be "readOnly" after the instance has fully loaded but I'm getting a Javascript error: Cannot call method 'setReadOnly' of null. When I dig into it, the error is ing from this line in the ckeditor.js, within the editor.setReadOnly method: this.editable().setReadOnly(a); That means that the editor exists, but the editable method/attribute (on the CKEditor instance) does not.

Below is my code, and I'll explain it a little. My app is a bination of GWT and Backbone. The CKEditor itself is created by the Backbone code but the parent element is in GWT so that's where I initiate the setEnabled action.

private native void setEnabledOnLoad(boolean enabled, String id) /*-{
  CKEDITOR.on("instanceReady", function(evt) {
    if(evt.editor.name === id) {
      Namespace.trigger(Namespace.Events.SET_ENABLED, enabled);
    }
  });
}-*/;

setEnabled: function(enabled) {
  this.editor.setReadOnly(!enabled);
  if(enabled){
    this.editor.focusManager.focus();
  } else {
    this.editor.focusManager.blur();
  }
}

The Backbone class has a listener for Namespace.Events.SET_ENABLED that triggers setEnabled.

Is there another CKEditor event that I should listen for? There doesn't appear to be an instanceReady event on editable. What am I missing?

EDIT
this.editor is created in the Backbone class render function like this:

this.editor = CKEDITOR.replace(this.$(this.id)[0], config);

The reason I don't add the instanceReady listener right after it's created is because the function setEnabledOnLoad is called in GWT before the instance has been fully initialized. This is a result of having the code in two places. GWT has said "ok, create the instance" but Backbone hasn't finished by the time GWT goes to the next line of code and wants to set it enabled/disabled.

Share Improve this question edited Aug 29, 2013 at 21:59 elemjay19 asked Aug 22, 2013 at 22:01 elemjay19elemjay19 1,1664 gold badges26 silver badges52 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 9

Two years later, but here is my solution. Maybe someone else will find it useful. As stated above, the event is appearantly triggered before the editable() function is fully set up, and therefore one solution is to simply wait for it to finish before setting it to readonly. This may be an ugly way to do it, but it works.

        //Delayed execution - ckeditor must be properly initialized before setting readonly
        var retryCount = 0;
        var delayedSetReadOnly = function () {
            if (CKEDITOR.instances['bodyEditor'].editable() == undefined && retryCount++ < 10) {
                setTimeout(delayedSetReadOnly, retryCount * 100); //Wait a while longer each iteration
            } else {
                CKEDITOR.instances['bodyEditor'].setReadOnly();
            }
        };
        setTimeout(delayedSetReadOnly, 50);

You could try subscribing to instanceReady event this way:

CKEDITOR.instances.editor.on("instanceReady", onInstanceReadyHandler) 

However, the editor instance must have been already created by then (inspect CKEDITOR.instances in the debugger).

I'm a bit confused about the difference between editable and editor. Could you show the fragments of your code where this.editor and this.editable get assigned?

[EDITED] I guess I see what's going on. CKEDITOR is a global object, you may think of it as of a class which holds all CKEDITOR instances. Trying to handle events with CKEDITOR.on isn't right, you need to do it on a specific instance (like I've shown above). I assume, "editor" is the ID of your parent element you want to attach a CKEDITOR instance to (please correct me if I'm wrong). I'm not familiar with Backbone, but usually it's done with replace:

var editorInstance = CKEDITOR.replace("editor", { on: { 
    instanceReady: function(ev) { alert("editor is ready!"); }}});

Here we attach a new instance of CKEDITOR to the editor parent element and subscribe to the instanceReady event at the same time. The returned object editorInstance should provide all the APIs you may need, including setReadOnly. You could also access it through the global CKEDITOR object using the parent element ID, i.e. CKEDITOR.instances.editor. On the other hand, editable is rather a service object available on editor. I can't think of any specific case where you might need to use it directly.

I apologize for never updating this with my solution. I needed to decouple the GWT function further from the CKEditor behavior. So, I added a function in GWT 'setEnabled' that is called from the parent object when it wants to update the enabled state of the CKEditor object.

public void setEnabled(boolean enabled) {
    this.enabled = enabled;
    toggleCKEditorEnabled(enabled);     
}       

Then changed the function referenced above 'setEnabledOnLoad' to be 'toggleCKEditorEnabled' which triggers the SET_ENABLED event with the enabled value.

Instead of attaching the listener to the specific instance of CKEditor, I added in to the Backbone MessageEntryView class that is the container of the CKEditor instance. In the initialize function of the MessageEntryView, I added this line

Namespace.on(Namespace.Events.SET_ENABLED, this.setEnabled);

This only works because I have one instance of CKEditor loaded on the screen at any given time. This problem and its solution stopped us from being able to add more CKEditor instances to the page at a time, which is something we discussed before moving on and replacing our whole client with Backbone.

发布评论

评论列表(0)

  1. 暂无评论