Observing the WordPress Customizer (wp.customize) Events

I needed to learn a little about the events triggered in the Customizer, and came up with a little script that prints events to the console log. This is specific to the Customizer’s event system.

//  js/fe-loader.js
jQuery(window).on("load", function() {

    monitor_events('wp.customize');
    monitor_events('wp.customize.previewer');
    monitor_events('wp.customize.control');
    monitor_events('wp.customize.section');
    monitor_events('wp.customize.panel');
    monitor_events('wp.customize.state');

    function monitor_events( object_path ) {
        var p = eval(object_path);
        var k = _.keys(p.topics);
        console.log( object_path + " has events ", k);
        _.each(k, function(a) {
            p.bind(a, function() {
                console.log( object_path + ' event ' + a, arguments );
            });
        });
    }
});

To use it, load the script via the customize_controls_init or customize_controls_enqueue_scripts action.

add_action( 'customize_controls_enqueue_scripts', 'fe_cc_scripts');
function fe_cc_scripts() {
    wp_register_script( 'fe-loader', plugin_dir_url(__FILE__)."/js/fe-loader.js", array('wp-backbone', 'wp-plupload'), wp_get_theme()->get('Version'), 1 );
    wp_enqueue_script( 'fe-loader' );
}

WP Customizer Events

WP created a new event system for the Customizer, so there are two distinct event systems running in WP: Backbone’s for the wp.media user interface tools, and WordPress’ for the Customizer.

The Customizer’s event system is built on jQuery’s $.Callbacks object. Callbacks implements a simple way to manage lists of callbacks, and was used to create a pub/sub or Observer system. The code is in customize-base.js.

My use case: listening to the preview updates.

So, this is the good news – it’s not that hard to understand. The bad news is that there isn’t an event that says “the preview iframe has completed loading”. I can’t use the jQuery(‘iframe’).on(‘load’, …) because the IFRAME isn’t loading at all. We’re reaching into the preview and altering the DOM.

(I want to catch the changes, and then re-insert some buttons that I add to the page when the page is in the Customizer preview. This is a somewhat redundant plugin that does some stuff that Customizer may do in the future — but imnso, is still worth doing.)

The previewer’s “ready” event seemed to be the one, but it fired just before the preview pane was updated. So I’d end up altering the DOM, then the DOM would be replaced with the preview content.

This needs to be explored further. Perhaps I need to spy on the events in the preview iframe as well.

Other interesting tidbits

Customizer implements it’s own class-based OO system, similar to goog.class. Extension is done via Classname.extend, but this is using the jQuery extend rather than Underscore’s extend. Generally, the library uses jQuery instead of Underscore.

Like Backbone, it has observable value objects, and collections of values.

There’s a Messenger base class that sends events over window.postMessage to another window or IFRAME. The Previewer uses it.

postMessage thoughts…

I’m not sure why they use this Messenger class or postMessage, when it’s probably safer to just reach into the IFRAME’s DOM: when you do that, the browser’s same-origin security rules are in effect. window.postMessage allows cross-domain messaging, and the burden of validating the origin and data contents are left to the programmer.

There must be a reason.

Eureka! The preview-ready and active events.

So, above, I said I needed to explore further. This was the result. This is a script that should load on the page in the preview IFRAME, so it’s added to all pages that are in the iframe. (I have to find out a better way to do this. Right now, I test the URI.) The stuff at the top is the code to spy on events.

The last part of the code attaches a spy on wp.customize.preview when it becomes available. It also attaches a listener on the “active” event, which I’m still trying to find the origins of.

(function () {
    console.log( 'wp.customize in preview frame', wp.customize);
    monitor_events('wp.customize');

    function monitor_events( object_path ) {
        var p = eval(object_path);
        if (p) {
            var k = _.keys(p.topics);
            console.log( object_path + " has events ", k);
            _.each(k, function(a) {
                p.bind(a, function() {
                    console.log( object_path + ' event ' + a, arguments );
                });
            });
        } else {
            console.log(object_path + ' does not exist');
        }
    }

    wp.customize.bind('preview-ready', function() {
        monitor_events('wp.customize.preview');
        wp.customize.preview.bind('active', fe_init_from_customizer);
    });
})();

Here’s the PHP part.

    wp_register_script( 'fe-customize', plugin_dir_url(__FILE__)."/js/fe-customize.js", array('fe-editors','customize-preview'), \wp_get_theme()->get('Version'), 1 );
    wp_enqueue_script( 'fe-customize' );

Just add it to a function that is called on the wp_enqueue_scripts action. The dependencies should force it to load after customize-preview.

The init code is called from the document in the preview, so we don’t need to use the on(“load”) trick from the previous blog post.

This event spy technique is interesting. You can watch the events fire as you edit values in the Customizer.

Leave a Reply