TinyMCE: How bind on event after its initialized
Asked Answered
D

5

13

I already searched a lot but by google-fu'ing don't get me any results :(

I have an already initialized tinyMCE editor which initialization process I cannot control, so code like the following don't work at all:

tinyMCE.init({
   ...
   setup : function(ed) {
          ed.onChange.add(function(ed, l) {
                  console.debug('Editor contents was modified. Contents: ' + l.content);
          });
   }
});

Code like the following doesn't work either since the jQuery tinymce plugin isn't defined:

$('textarea').tinymce({
    setup: function(ed) {
        ed.onClick.add(function(ed, e) {
            alert('Editor was clicked: ' + e.target.nodeName);
        });
    }
});

I mean, it has to be using the tinymce.something syntax.

How can I bind a callback function to whatever tinyMCE event after tinyMCE is already initialized?

Dedrick answered 29/8, 2013 at 15:24 Comment(0)
D
12

After hacking into the tinymce object with console.log(), I found a working solution:

setTimeout(function () {
       for (var i = 0; i < tinymce.editors.length; i++) {
             tinymce.editors[i].onChange.add(function (ed, e) {
             // Update HTML view textarea (that is the one used to send the data to server).
         ed.save();
       });
            }
}, 1000);

Inside that callback function one can set the whatever event binding one wants.

The setTimeout call is to overcome the racing condition of tinymce and jQuery, since when the call to tinymce.editors[i].onChange.add() is made tinymce wasn't initialized yet.

Dedrick answered 29/8, 2013 at 16:22 Comment(1)
In my implementation this code was 90% of the solution, but it still gave me the warning "Deprecated TinyMCE API call: <target>.onChange.add(...)" - the remaining 10% was the fix which is the replacement of onChange.add(function() {}) with on('change', function() {}) - just as Ed Zavade suggested below.Mica
I
17

Although this post is old now but I think other people will need this answer. I had the same problem and to fix it I did this:

tinyMCE.init({
        ...
        init_instance_callback : "myCustomInitInstance"
});

Based on http://www.tinymce.com/wiki.php/Configuration3x:init_instance_callback

Ipswich answered 20/1, 2014 at 16:11 Comment(0)
D
12

After hacking into the tinymce object with console.log(), I found a working solution:

setTimeout(function () {
       for (var i = 0; i < tinymce.editors.length; i++) {
             tinymce.editors[i].onChange.add(function (ed, e) {
             // Update HTML view textarea (that is the one used to send the data to server).
         ed.save();
       });
            }
}, 1000);

Inside that callback function one can set the whatever event binding one wants.

The setTimeout call is to overcome the racing condition of tinymce and jQuery, since when the call to tinymce.editors[i].onChange.add() is made tinymce wasn't initialized yet.

Dedrick answered 29/8, 2013 at 16:22 Comment(1)
In my implementation this code was 90% of the solution, but it still gave me the warning "Deprecated TinyMCE API call: <target>.onChange.add(...)" - the remaining 10% was the fix which is the replacement of onChange.add(function() {}) with on('change', function() {}) - just as Ed Zavade suggested below.Mica
T
3

EDIT: oops - I thought this was another question I was looking at that was specific to WordPress + TinyMCE, guess not. Though I'll leave the answer here as it may be helpful to others.

The proper way to do this would be to append to the tinyMCE init with the WordPress filter. For example:

PHP (in functions.php or other location that is run on edit page load):

  add_action( 'init', 'register_scripts' );     
  function register_scripts(){
    if ( is_admin() ) {
      wp_register_script(
        'admin-script',
        get_stylesheet_directory_uri() . "/js/admin/admin.js",
        array('jquery'),
        false,
        true
      );
    }
  }

  add_action( 'admin_enqueue_scripts', 'print_admin_scripts' );
  function print_admin_scripts() {        
    wp_enqueue_script('admin-script');
  }

  add_filter( 'tiny_mce_before_init', 'my_tinymce_setup_function' );
  function my_tinymce_setup_function( $initArray ) {
    $initArray['setup'] = 'function(ed){
      ed.onChange.add(function(ed, l) {
        tinyOnChange(ed,l);
      });
    }';
    return $initArray;
  }

JavaScript (/js/admin/admin.js)

  (function($){
    window.tinyOnChange = function(ed, l){        
      console.log('Editor contents was modified. Contents: ' + l.content);      
    }
  }(jQuery);

This is tested and working in WordPress 3.9 (though I am simply getting the console output:

Deprecated TinyMCE API call: <target>.onChange.add(..) 

But that's due to tinyMCE deprecating code you're trying to use. This method does still work to modify to any of the tinyMCE init options - I'm currently using it for init_instance_callback and it's working great.

-Thomas

Trite answered 30/7, 2014 at 18:50 Comment(1)
I found this very helpfull, So thanks for leaving itCheddite
S
3

In TinyMCE 4, onChange doesn't exist. You have to use:

tinymce.get('myeditorname').on("change", function() { alert("stuff"); });
Statue answered 12/10, 2016 at 17:30 Comment(1)
IMO the best answer on how to add an onChange event after editor(s) have been initialised.Relativity
J
3

Here is my solution for binding events to one or multiple textareas at any time, given this code is appended after including the tinymce javascript file into your page. (In other words, the only thing required for this to work, is the existence of the 'tinymce' variable)

// Loop already initialised editors
for(id in tinymce.editors){
    if(id.trim()){
        elementReady(id);
    }
}

// Wait for non-initialised editors to initialise
tinymce.on('AddEditor', function (e) {
    elementReady(e.editor.id);
});

// function to call when tinymce-editor has initialised
function elementReady(editor_id){

    // get tinymce editor based on instance-id
    var _editor = tinymce.editors[editor_id];

    // bind the following event(s)
    _editor.on("change", function(e){

        // execute code
    });
}

Note that the 'change' event is not bound to always trigger instantly after something changed. According to the documentation, it “gets fired when changes are made inside the editor that cause an undo level to be added.” Which in my experience, does not always happen when you expect it to happen.

One way to overcome this is by binding multiple events, such as 'input change', however, there will be some overlapping, which should then be filtered.

Jeter answered 13/3, 2017 at 14:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.