Improvements to the Customize JS API in 4.9

There are many user-facing CustomizerCustomizer Tool built into WordPress core that hooks into most modern themes. You can use it to preview and modify many of your site’s appearance settings. improvements in 4.9, including: drafting/scheduling of changesets, autosave revisionsRevisions The WordPress revisions system stores a record of each saved draft or published update. The revision system allows you to see what changes were made in each revision by dragging a slider (or using the Next/Previous buttons). The display indicates what has changed in each revision., changeset post locking, frontend public previews, a new experience for browsing and installing themes, updated nav menu creation UXUX User experience, and the code editing improvements for the Custom HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. widgetWidget A WordPress Widget is a small block that performs a specific function. You can add these widgets in sidebars also known as widget-ready areas on your web page. WordPress widgets were originally created to provide a simple and easy-to-use way of giving design and structure control of the WordPress theme to the user. and Additional CSSCSS Cascading Style Sheets.But in addition to all of these, there are also many improvements for developers which will make extending the Customizer much more pleasant.

Something important to remember about the Customizer is that it is a single page application that is powered by JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/.. Many developers may only interact with the PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. for registering controls, settings, sections, panels, and partials. But controls, sections, and panels do not need to be registered in PHP at all. The PHP API for registration is essentially a wrapper for the underlying JSJS JavaScript, a web scripting language typically executed in the browser. Often used for advanced user interfaces and behaviors. API. When you load the Customizer all of the params for the PHP-registered constructs are exported to the client for the JavaScript API to instantiate and initially add to the UIUI User interface, but this JS API can dynamically instantiate additional constructs at any time thereafter in a Customizer session. This is how new widgets, nav menus, and nav menu items are added without requiring the entire Customizer to reload. You can also avoid statically registering settings and partials in PHP by instead adding filters to dynamically recognize settings and partials, allowing them to be registered on demand. All of this allows the Customizer application to scale out to be able to customize and preview an unlimited number of things on a site (e.g. any post or page with their postmeta in the Customize Posts feature pluginFeature Plugin A plugin that was created with the intention of eventually being proposed for inclusion in WordPress Core. See Features as Plugins.). The point here is that in order for the Customizer to scale, the JavaScript API must be used directly. So this is why the Customizer JS API improvements in 4.9 are important as they fix many longstanding annoyances and shortcomings with the JS API.

This dev notedev note Each important change in WordPress Core is documented in a developers note, (usually called dev note). Good dev notes generally include a description of the change, the decision that led to this change, and a description of how developers are supposed to work with that change. Dev notes are published on Make/Core blog during the beta phase of WordPress release cycle. Publishing dev notes is particularly important when plugin/theme authors and WordPress developers need to be aware of those changes.In general, all dev notes are compiled into a Field Guide at the beginning of the release candidate phase. contains the following sections:

Elimination of Repeated ID when Adding Constructs

Previously in the Customizer when you wanted to add a new construct dynamically you would need to pass the ID into the constructor itself and then pass the ID in when adding it to a collection as well. This was unfortunate as it required keeping multiple references to the same ID in sync, for example:

/* Before WordPress 4.9 */
wp.customize.control.add(
    'foo',
    new wp.customize.Control( 'foo', { /* … */ } )
);

Or to store the identifier in a variable to re-use, as the two should never differ:

/* Before WordPress 4.9 */
var code = 'too_short';
wp.customize.control( 'blogname' ).notifications.add(
    code, 
    new wp.customize.Notification( code, {
        message: 'Site title too short.'
    } ) 
);

In 4.9 this is no longer required, as the ID will be automatically read from the object being passed to the add method. So now you can simply do:

/* Since WordPress 4.9 */
wp.customize.control.add(
    new wp.customize.Control( 'foo', { /* … */ } ) 
);

And:

/* Since WordPress 4.9 */
wp.customize.control( 'blogname' ).notifications.add(
    new wp.customize.Notification( 'too_short', {
        message: 'Site title too short.'
    } ) 
);

Naturally the change here to the add method is backwards-compatible, and existing code that passes an ID as the first argument will still continue to work.

Flattened Constructor Parameters

When creating a setting in JavaScript, the supplied options argument is simply a 1-level deep object:

var setting = new wp.customize.Setting( 'foo', value, {
    transport: 'postMessage',
    /* ... */
} );

However, when constructing  something else—controls, sections, panels, or partials—the supplied options object required the properties to be wrapped in an intermediary params property like this:

/* Before WordPress 4.9 */
var control = new wp.customize.Control( 'bar', {
    params: {
        section: 'title_tagline'
        /* ... */
    }
} );

This was annoying and the params property has been unwrapped to allow you to pass the props in the root options object:

/* Since WordPress 4.9 */
var control = new wp.customize.Control( 'bar', {
    section: 'title_tagline'
    /* ... */
} );

Backwards compatibility is maintained, so existing code that passes props via options.params will continue to be read from first, and then fall back to the new unwrapped options for props. The options.params property is soft-deprecated in 4.9 (using it will raise no warning) but it may be hard-deprecated in the future.

Default Parameters

Another longstanding annoyance with adding constructs in the Customizer is that there was a lack of parameter defaults. When creating a Color control, for example, the following is the minimum required prior to 4.9:

/* Before WordPress 4.9 */
var control = new wp.customize.ColorControl( 'favorite_color', {
    params: {
        type: 'color',
        content: '<li class="customize-control customize-control-color"></li>',
        priority: 10,
        active: true,
        section: 'colors',
        label: 'Favorite Color',
        settings: { 'default': 'favorite_color' },
    }
} );
wp.customize.control.add( 'favorite_color', control );

This is a lot of code, considering that in PHP all that a user would have to do is:

$control = new WP_Customize_Color_Control( $wp_customize, 'favorite_color', array(
    'section' => 'colors',
    'label' =>'Favorite Color',
    'settings' => array( 'default' => 'favorite_color' ),
) );
$wp_customize->add_control( $control );

The type, content, priority, and active properties have defaults in PHP and so too they should have defaults in JS. And now they do. Creating a Color control in JS, combined with the flattening of params above, is now just:

/* Since WordPress 4.9 */
var control = new wp.customize.ColorControl( 'favorite_color', {
    section: 'colors',
    label: 'Favorite Color',
    settings: { 'default': 'favorite_color' },
} );
wp.customize.control.add( control );

So creating controls in JS is now pretty much identical to creating them in PHP, aside from the differences in syntax.

Beyond controls, defaults are also available for settings, sections, panels, and partials. For settings, defaults are provided for transport (refresh) and previewer (which should never vary):

/* Before WordPress 4.9 */ 
var setting = new wp.customize.Setting( 'favorite_color', '#000000', {
    transport: 'refresh',
    previewer: wp.customize.previewer
} );
wp.customize.add( 'favorite_color', setting );
/* Since WordPress 4.9 */
var setting = new wp.customize.Setting( 'favorite_color', '#000000' );
wp.customize.add( setting );

On all constructs (controls, settings, panels, sections, partials) there is a defaults property on their function prototypes which contains the default params. This can be leveraged to add new defaults to subclasses, in addition to any extended methods. For example:

var ColorSetting = wp.customize.Setting.extend({
    defaults: _.extend(
        {},
        wp.customize.Setting.prototype.defaults,
        {
            transport: 'postMessage'
        }
    ),

    validate: function( value ) {
        var setting = this, validatedValue;
        validatedValue = wp.customize.Setting.prototype.validate.call( setting, value );
        if ( null === validatedValue ) {
            return validatedValue;
        }
        if ( /^#\d{6}$/.test( validatedValue ) ) {
            setting.notifications.remove( 'invalid_color' );
        } else {
            setting.notifications.add( new wp.customize.Notification( 'invalid_color', {
                message: 'Bad color format'
            } ) );
        }
        return validatedValue;
    }
});

When this ColorSetting is instantiated, the transport of postMessage will not need to be explicitly provided each time.

Passing Settings when Instantiating Controls

When instantiating controls in JS prior to 4.9, you had to pass settings as an object mapping a setting key to the setting ID. For example, to add a control for manipulating the site title, you would do:

var blognameControl = new wp.customize.Control( 'blogname2', {
    /* ... */
    settings: {
        'default': 'blogname'
    }
} );

If a control is manipulating multiple settings, then the keys for the settings object will get additional values:

var bgPositionControl = new wp.customize.BackgroundPositionControl( 'mobile_bg_position', {
   /* ... */
   settings: {
      x: 'mobile_background_position_x',
      y: 'mobile_background_position_y'
   }
} );

The settings object here is really just indicating which settings the control depends on, so that it can defer initialization until all of the settings are created. Prior to 4.9, the keys served no purpose other than to give a symbolic reference to a given setting. When a default setting is used, then it also can be accessed directly via a setting shortcut. For example:

var xValue = bgPositionControl.settings.x.get();
var title = blognameControl.settings['default'].get();
var title2 = blognameControl.setting.get();

In 4.9, the way settings are passed to controls is getting revamped. Additionally, the symbolic keys supplied for the settings are now more useful in that they can be used to create input element links.

First of all, if you have a control that just takes one setting, you can now just pass a single setting:

var control = new wp.customize.Control( 'blogname2', {
    /* ... */
    setting: 'blogname'
} );

Additionally, just as with partials you can pass settings as an array (#36167), where the first item then becomes the default.

Furthermore, instead of passing a setting’s string ID you can now pass an existing Setting object, promoting better encapsulation (#37964):

var control = new wp.customize.Control( 'blogname2', {
    /* ... */
    setting: wp.customize( 'blogname' )
} );

Where this gets particularly important is that you can also pass Value instances as settings instead of registering a Setting, which would then attempt to get saved in the changeset (and fail as being unrecognized, without static of dynamic server-side registration). In other words, a control (view) can be supplied a model which is either a Setting or a Value (and the former is a subclass of the latter). This is how the controls for the changeset status and date are constructed. For example, to create a control which manages the sidebarSidebar A sidebar in WordPress is referred to a widget-ready area used by WordPress themes to display information that is not a part of the main content. It is not always a vertical column on the side. It can be a horizontal rectangle below or above the content area, footer, header, or any where in the theme. width, one could do (which also shows how to add a control to the new Publish Settings section):

wp.customize.control.add( new wp.customize.Control( 'pane_width', {
    type: 'number',
    section: 'publish_settings',
    setting: new wp.customize.Value( 300 ),
    label: 'Pane Width',
    priority: 110,
    input_attrs: {
        min: 150,
        max: 1000
    }
} ) );

A pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party could then listen for changes to that control’s setting model to then perhaps store the value in localStorage to persist the user preference for how wide they want their sidebar to be. Being able to construct controls with Value models is a next iteration beyond setting-less controls introduced in 4.5.

Prior to 4.9, in spite of having these symbolic references to a control’s settings the control’s content would require the actual setting ID to be used in any input element links (the bidirectional data binding between a control’s setting and input). For example, the site title control has a default setting pointing to blogname, but in spite of this, the content for the control would contain:

<input type="text" data-customize-setting-link="blogname">

The fact that data-customize-setting-link required an actual setting ID to be used as its value made it difficult to create reusable control templates. This now has been improved in 4.9 by allowing the setting keys to be used in data-customize-setting-key-link attributes:

<input type="text" data-customize-setting-key-link="default">

The WP_Customize_Control::get_link() PHP method has been updated to output a data-customize-setting-key-link attribute with the supplied setting key as its value when the associated setting does not exist. Otherwise, it maintains its prior behavior of using the setting ID as the value for a data-customize-setting-link attribute. The JS logic responsible for linking inputs with settings via elements is now contained in a new linkElements method on Control.

Control Templates

Refer back to the “Pane Width” example above where a type of number was supplied when constructing a base wp.customize.Control class. This would not work prior to 4.9 because there were no content templates defined for base control types. This is no longer the case with the resolution of #30738. If you construct a control and supply a type param that corresponds to an HTML5 input type, then the default control template will be used to render the control. Note that for color inputs, one should use the dedicated ColorControl.

A control’s template is normally derived by its type; for instance, a Background Position control has a type of background_position and then has a wp.template ID of customize-control-background_position-content. The template is normally output by a control’s WP_Customize_Control::content_template() template after the control has been registered via WP_Customize_Manager::register_control_type(). In 4.9 however, this can all be bypassed as controls now understand a templateId param.

An example of supplying an ad hoc templateId when constructing a control can be seen in the changeset status control, where it overrides the template used for this one radio control instance:

var statusControl = new api.Control( 'changeset_status', {
    priority: 10,
    type: 'radio',
    section: 'publish_settings',
    setting: api.state( 'selectedChangesetStatus' ),
    templateId: 'customize-selected-changeset-status-control',
    label: api.l10n.action,
    choices: api.settings.changeset.statusChoices
} );
api.control.add( statusControl );

Alternatively, if you have a custom control that you always want to have a specific template (as when calling WP_Customize_Manager::register_control_type() in PHP), you can now do this entirely in JS by adding templateId to the control subclass’s defaults, as can be seen in the PreviewLinkControl:

var PreviewLinkControl = api.Control.extend({
   defaults: _.extend(
      {},
      api.Control.prototype.defaults,
      {
         templateId: 'customize-preview-link-control'
      }
   )
   /* ... */
});

For some more examples, see answer on the WordPress Development Stack Exchange.

Speaking of WP_Customize_Manager::register_control_type(), the types for panels, sections, and controls now are all registered before the customize_register action is triggered. This means that if you have a plugin that unregisters all customize_register actions in order to create a “blank slate” plugin-specific Customizer, you no longer have to re-register these types to be able to use them.

Date/Time Control

In support of the 4.9 feature for scheduling changesets for publishing (#28721), there is a new control for representing a date/time. The control shows fields for editing the date with optional fields also for editing the time. The control syncs to a setting a date in the Y-m-d H:i:s format. The time in the control can either be presented in 12-hour format or 24-hour format. Additionally, the control can specify that it requires a future date. The PHP class is WP_Customize_Date_Time_Control and the JS class is wp.customize.DateTimeControl. Here is how to create a few of the controls in JS with screenshots for how they appear:

wp.customize.control.add( new wp.customize.DateTimeControl( 'birthdate', {
    label: 'Birthdate',
    description: "Someone was born on this day.",
    section: sectionId,
    includeTime: false,
    setting: new wp.customize.Value( '2000-01-02' )
} ) );