The Customizer 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. is the first true JS-driven feature in core. That’s awesome, especially coming out of WCSF where JavaScript 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/. has been highlighted so prominently between Backbone.js, the WP REST API, Node.js, and socket.io. The Customizer has a great architecture with models for settings, controls, watchable-values, collections, events, and asynchronous callbacks. Nevertheless, the JavaScript API 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. in the Customizer is incomplete.
Existing Challenges
When widgets were added to the Customizer in 3.9, adding widget 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. controls to sidebar 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. sections required direct DOM manipulation. For controls there is at least a Control model to manage state For sections and panels, however, there are no JS JavaScript, a web scripting language typically executed in the browser. Often used for advanced user interfaces and behaviors. models at all, and so adding them dynamically is even more of a challenge. And this is the exact challenge that Nick Halsey’s Menu Customizer plugin 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 currently has to struggle through.
When Customizer panels were added in 4.0 to group all widget area sections into a “Widgets” panel, a bug was introduced whereby shift-clicking a widget in the preview no longer revealed the widget control in the Customizer pane because the sections were inside of the collapsed Widgets panel: there were no models to represent the state for whether or not a panel or section were currently expanded. Without models, a fix of this would require more messy DOM traversal to check if parent accordion sections were expanded or not. Storing data in the DOM is bad.
In 4.0 the concept of contextual controls were added to the Customizer. This allowed controls to be registered with an active_callback
, such as is_front_page
, which the preview would execute and pass back up to the Customizer pane to then show/hide the control based on which URL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org was being previewed. This worked well, except when all controls in a section were inactive: then the result was a Customizer section without any visible controls. Instead, the expected behavior would be for the section to automatically collapse as well when all controls inside become inactive. Again, this problem stems from the fact that there is no JS model to represent a section and to list out the controls associated with it.
For the past three weeks I’ve been focused on fleshing out the Customizer API to address these challenges, and to facilitate doing new dynamic things in the Customizer. The parent ticket Created for both bug reports and feature development on the bug tracker. for this is #28709: Improve/introduce Customizer JS models for Controls, Sections, and Panels.
Models for Panels and Sections
As noted above, there is a wp.customize.Control
model, and then there is a wp.customize.control
collection (yes, it is singular) to store all control instances. So to follow the pattern established by controls, in the patch there is a wp.customize.Panel
and wp.customize.Section
, along with wp.customize.panel
and wp.customize.section
collections (both singular again). So just as with controls, you can iterate over panels and sections via:
wp.customize.panel.each( function ( panel ) { /* ... */ } );
wp.customize.section.each( function ( section ) { /* ... */ } );
Relating Controls, Sections, and Panels together
When registering a new control in PHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher, you pass in the parent section ID:
$wp_customize->add_control( 'blogname', array(
'label' => __( 'Site Title' ),
'section' => 'title_tagline',
) );
In the proposed JavaScript API, a control’s section can be obtained predictably:
id = wp.customize.control( 'blogname' ).section(); // => title_tagline
To get the section object from the ID, you just look up the section by the ID as normal: wp.customize.section( id )
.
You can move a control to another section using this section
state as well, here moving it to the Navigation section:
wp.customize.control( 'blogname' ).section( 'nav' );
Likewise, you can get a section’s panel ID in the same way:
id = wp.customize.section( 'sidebar-widgets-sidebar-1' ).panel(); // => widgets
You can go the other way as well, to get the children of panels and sections:
sections = wp.customize.panel( 'widgets' ).sections();
controls = wp.customize.section( 'title_tagline' ).controls();
You can use these to move all controls from one section to another:
_.each( wp.customize.section( 'title_tagline' ).controls(), function ( control ) {
control.section( 'nav' );
});
Contextual Panels and Sections
Also just as with controls, when you invoke $wp_customize->add_section()
you can pass an active_callback
param to indicate whether the section is relevant to the currently-previewed URL; the same goes for panels. A good example of a contextual section is only showing the “Static Front Page A WordPress website can have a dynamic blog-like front page, or a “static front page” which is used to show customized content. Typically this is the first page you see when you visit a site url, like wordpress.org for example.” section if the preview is currently on the front-page:
function contextual_static_front_page_section( $wp_customize ) {
$wp_customize->get_section( 'static_front_page' )->active_callback = 'is_front_page';
}
add_action( 'customize_register', 'contextual_static_front_page_section', 11 );
Nevertheless, this will not usually be needed because a section inherits its active state from its control children (and a panel inherits from its section children), via the new isContextuallyActive()
method. If all controls within a section become inactive, then the section will automatically become inactive.
As with controls, Panel
and Section
instances have an active
state (a wp.customize.Value
instance). When the active
state changes, the panel, section, and control instances invoke their respective onChangeActive
method, which by default slides the container element up and down, if false
and true
respectively. There are also activate()
and deactivate()
methods now for manipulating this active
state, for panels, sections, and controls:
wp.customize.section( 'nav' ).deactivate(); // slide up
wp.customize.section( 'nav' ).activate({ duration: 1000 }); // slide down slowly
wp.customize.section( 'colors' ).deactivate({ duration: 0 }); // hide immediately
wp.customize.section( 'nav' ).deactivate({ completeCallback: function () {
wp.customize.section( 'colors' ).activate(); // show after nav hides completely
} });
Note that manually changing the active
state would only stick until the preview refreshes or loads another URL. The activate()
/deactivate()
methods are designed to follow the pattern of the new expanded
state.
Expanded State
As noted above, in 4.0 when panels were introduced, a bug was introduced whereby shift-clicking a widget in the preview fails to show the widget control if the Widgets panel is not already open. With the proposed changes, panels, sections, and (widget) controls have an expanded
state (another wp.customize.Value
instance). When the state changes, the onChangeExpanded
method is called which by will handle Panels sliding in and out, and sections sliding up and down (and widget controls up and down, as they are like sections). So now when a widget control needs to be shown, the control’s section and panel can simply have their expanded
state to true in order to reveal the control. Expanding a section automatically expands its parent panel. Expanding a widget control, automatically expands its containing section and that section’s panel.
As with activate()
/deactivate()
to manage the active
state, there are expand()
and collapse()
methods to manage the expanded
state. These methods also take a similar params
object, including duration
and completeCallback
. The params
object for Section.expand()
accepts an additional parameter “allowMultiple
” to facilitate dragging widget controls between sidebar sections. By default expanding one section will automatically collapse all other open sections, and so this param overrides that. You can use this, for instance, to expand all sections at once so you can see all controls without having to click to reveal each accordion section one by one:
wp.customize.section.each(function ( section ) {
if ( ! section.panel() ) {
section.expand({ allowMultiple: true });
}
});
Focusing
Building upon the expand()
/collapse()
methods for panels, sections, and controls, these models also support a focus()
method which not only expands all of the necessary element, but also scrolls the target container into view and puts the browser focus on the first focusable element in the container. For instance, to expand the “Static Front Page” section and focus on select dropdown for the “Front page”:
wp.customize.control( 'page_on_front' ).focus()
This naturally fixes the #29529, mentioned above.
The focus functionality is used to implement autofocus: deep-linking to panels, sections, and controls inside of the customizer. Consider these URLs:
- …/wp-admin (and super admin)/customize.php?autofocus[panel]=widgets
- …/wp-admin/customize.php?autofocus[section]=colors
- …/wp-admin/customize.php?autofocus[control]=blogname
This can be used to add a link on the widgets admin page to link directly to the widgets panel within the Customizer.
Priorities
When registering a panel, section, or control in PHP, you can supply a priority
parameter. This value is now stored in a wp.customize.Value
instance for each respective Panel
, Section
, and Control
instance. For example, you can obtain the priority for the widgets panel via:
priority = wp.customize.panel( 'widgets' ).priority(); // => 110
You can then dynamically change the priority and the Customizer panel will automatically re-arrange to reflect the new priorities:
wp.customize.panel( 'widgets' ).priority( 1 ); // move Widgets to the top
Custom types for Panels and Sections
Just as Customizer controls can have custom types (ColorControl
, ImageControl
, HeaderControl
…) which have custom behaviors in JS:
wp.customize.controlConstructor.FooControl = wp.customize.Control.extend({ /*...*/ });
So too can Panels and Sections have custom behaviors in the proposed changes. A type
parameter can be passed when creating a Panel or Section, and then in JavaScript a constructor corresponding to that type can be registered. For instance:
PHP:
add_action( 'customize_register', function ( $wp_customize ) {
class WP_Customize_Harlem_Shake_Section extends WP_Customize_Section {
public $type = 'HarlemShake';
}
$section = new WP_Customize_Harlem_Shake_Section(
$wp_customize,
'harlem_shake',
array( 'title' => __( 'Harlem Shake' ) )
);
$wp_customize->add_section( $section );
$wp_customize->add_setting( 'harlem_shake_countdown', array(
'default' => 15,
));
$wp_customize->add_control( 'harlem_shake_countdown', array(
'label' => __( 'Countdown' ),
'section' => 'harlem_shake',
'setting' => 'harlem_shake_countdown',
'type' => 'number',
));
});
JS:
wp.customize.sectionConstructor.HarlemShake = wp.customize.Section.extend({
shake: function () {
// This can be invoked via wp.customize.section( 'harlem_shake' ).shake();
console.info( 'SHAKE!!' );
}
});
Next Steps
- Continue discussion on parent ticket #28709: Improve/introduce Customizer JS models for Controls, Sections, and Panels.
- Review JavaScript changes in pull request. Anyone is free to open a PR to onto the existing branch A directory in Subversion. WordPress uses branches to store the latest development code for each major release (3.9, 4.0, etc.). Branches are then updated with code for any minor releases of that branch. Sometimes, a major version of WordPress and its minor versions are collectively referred to as a "branch", such as "the 4.0 branch". on GitHub GitHub is a website that offers online implementation of git repositories that can easily be shared, copied and modified by other developers. Public repositories are free to host, private repositories require a paid subscription. GitHub introduced the concept of the ‘pull request’ where code changes done in branches by contributors can be reviewed and discussed before being merged be the repository owner. https://github.com/ to make changes. Props to Ryan Kienstra (@ryankienstra) and Nick Halsey (@celloexpressions) for their contributions.
- Update logic for adding widget controls to use new API (adding widgets is using the old pseudo-API and it is currently broken); allow controls to be added manually.
- Work with Nick Halsey to make sure that dynamically-created sections and controls suit the needs of Menu Customizer, and make sure that it works for other plugins like Customize Posts.
- Build upon the initial QUnit tests to add coverage for existing JS API and newly added API (#28579).
- Harden the logic for animating the Customizer panel into view.
- Get feedback from other Core Core is the set of software required to run WordPress. The Core Development Team builds WordPress. devs and get initial patch A special text file that describes changes to code, by identifying the files and lines which are added, removed, and altered. It may also be referred to as a diff. A patch can be applied to a codebase for testing. committed.
Thanks to Nick Halsey (@celloexpressions) for his proofreading and feedback on the drafts of this blog (versus network, site) post.
#customize, #menu-customizer, #menus, #widget-customizer