Today we’ll take a look customizing the Customizer user experience. Say that 5 times fast!
It is important to note that the following code snippets will function if placed in a child theme, however they are only presented as examples and may not be ready for production environments. If you’re planning on using these examples in a plugin or theme, you’ll want to make the necessary modifications (i.e. prefixing functions and variables, optimizing code snippets, etc…).
In our previous posts, we’ve covered communication between the Previewer window and the Customizer, as well as adding helpful buttons to the Previewer. Today we’ll discuss more practical uses for these buttons and ways that you can customize the user experience within the Customizer.
In this post, you’ll learn how to open the Customizer Sidebar, open the Widgets Panel, open the Primary Sidebar Section, and open the Add a Widget “Panel”. We’ll be building off of the scripts that we created in the previous posts.
Preface: WordPress 4.1 Customizer API Updates
With the release of WordPress 4.1, there was a shift towards improving Customizer functionality. These new improvements help us greatly when it comes to interacting with the Customizer.
Previously, in Conductor, we had to create our own logic to open Panels and Sections. In WordPress 4.1, we can use new functionality built into the Customizer JavaScript API to accomplish a lot of these tasks. Kudos to the core WordPress development team!
Please Note: Some of the following code snippets will only work with WordPress 4.1 and above.
More Helper Buttons
If you’ve been following along, we currently have two helper buttons that are added to the Previewer via JavaScript. To kick off this tutorial, we’re going to add a few additional helper buttons to the Previewer. The first button that we are going to add is a button to open the Customizer Sidebar. The other buttons will open a Panel within the Customizer, open a Section within a Panel, and allow a widget to be added to a sidebar.
Add the following code to the ‘active’ event callback function in the customizer-previewer.js
script:
// Append "Open Customizer Sidebar" button to the <body> element | |
$body.append( ' <button class="my-previewer-event-button my-previewer-open-customizer-sidebar" data-customizer-event="my-open-customizer-sidebar">Open Customizer Sidebar</button>' ); | |
// Append "Open Widgets Panel" button to the <body> element | |
$body.append( ' <button class="my-previewer-event-button my-previewer-open-widgets-panel" data-customizer-event="my-open-widgets-panel">Open Widgets Panel</button>' ); | |
// Append "Open Primary Sidebar Section" button to the <body> element | |
$body.append( ' <button class="my-previewer-event-button my-previewer-open-primary-sidebar-section" data-customizer-event="my-open--primary-sidebar-section">Open Primary Sidebar Section</button>' ); | |
// Append "Open Add a Widget Panel" button to the <body> element | |
$body.append( ' <button class="my-previewer-event-button my-previewer-open-add-widget-panel" data-customizer-event="my-open-add-widget-panel">Open Add a Widget Panel</button>' ); | |
// Append "Open Everything" button to the <body> element; performs all of the previous button actions simultaneously | |
$body.append( ' <button class="my-previewer-event-button my-previewer-open-everything" data-customizer-event="my-open-everything">Open Everything</button>' ); |
Triggering the Events
Now we have to listen for touch/click events on these new buttons. Just like we did in the last post, we’ll listen to the document and then send the event over to the Customizer upon click.
Add the following code to the ‘active’ event callback function in the customizer-previewer.js
file after the first snippet above:
// Listen for events on the new previewer buttons | |
$document.on( 'touch click', '.my-previewer-event-button', function( e ) { | |
var $this = $( this ); | |
// Send the event that we've specified on the HTML5 data attribute ('data-customizer-event') to the Customizer | |
self.preview.send( $this.attr( 'data-customizer-event' ) ); | |
} ); |
If you were to load the Customizer with these scripts in place, you’d see the new buttons added to the end of the page in the Previewer window. As with the previous post, these buttons will be pretty useless at this point in time since we haven’t set up the event listeners on the Customizer. We’ll fix that in the next step.
“Open Customizer Sidebar” Event Listener
The first event listener that we’ll add functionality for is for the “Open Customizer Sidebar” event. The Customizer Sidebar is one of the only aspects of the Customizer that doesn’t currently have a “state” attached to it so we cannot use built-in functions to expand/collapse it. Instead, we’ll use our own logic to open the sidebar.
Add the following snippet to the ‘init’ function in the myCustomizerPreviewer object in the Customizer (customizer.js
) script:
// Listen to the "my-open-customizer-sidebar" event has been triggered from the Previewer | |
this.preview.bind( 'my-open-customizer-sidebar', function() { | |
alert( '"my-open-customizer-sidebar" has been received from the Previewer.' ); | |
// If the "overlay" (Customizer Sidebar) is collapsed, open it | |
if ( $( '.wp-full-overlay' ).hasClass( 'collapsed' ) ) { | |
// Trigger a click event on the collapse sidebar element | |
$( '.collapse-sidebar' ).trigger( 'click' ); | |
} | |
// Otherwise the Customizer Sidebar is already expanded (open) | |
else { | |
alert( 'Customizer Sidebar is already open.' ); | |
} | |
} ); |
alert()
if the sidebar is already open.
“Open Widgets Panel” Event Listener
Next we’ll add an event listener for the “Open Widgets Panel” event. Since Panels are stored in a collection, we can access them using API functionality.
Add the following snippet to the ‘init’ function after the snippet above:
// Listen to the "my-open-widgets-panel" event has been triggered from the Previewer | |
this.preview.bind( 'my-open-widgets-panel', function() { | |
// Grab the widgets panel from the collection of panels | |
var widgets_panel = wp.customize.panel( 'widgets' ); | |
alert( '"my-open-widgets-panel" has been received from the Previewer.' ); | |
// If the Widgets Panel is collapsed, open it | |
if ( ! widgets_panel.expanded() ) { | |
widgets_panel.expand(); | |
} | |
// Otherwise the Widgets Panel is already expanded (open) | |
else { | |
alert( 'Widgets Panel is already open.' ); | |
} | |
} ); |
The code above will open the Widgets Panel if it is not already open or will pop up an alert()
if it is already open.
“Open Primary Sidebar” Event Listener
This bit of code assumes your WordPress instance has a sidebar with an ID of primary-sidebar
registered. Since Sections have been stored in a collection prior to WordPress 4.1, we can also access them using API functionality. One of the cool things about a nested Section (a Section within a Panel) is that once they are expanded (.expand()
), their parent (Panel) is also expanded when using the Customizer JavaScript API.
Add the following snippet to the ‘init’ function after the snippet above:
// Listen to the "my-open-primary-sidebar" event has been triggered from the Previewer | |
this.preview.bind( 'my-open-primary-sidebar', function() { | |
var primary_sidebar_section = wp.customize.section( 'sidebar-widgets-primary-sidebar' ); // Grab the Primary Sidebar from the collection of panels | |
alert( '"my-open-primary-sidebar" has been received from the Previewer.' ); | |
// If the Primary Sidebar is collapsed, open it | |
if ( ! primary_sidebar_section.expanded() ) { | |
primary_sidebar_section.expand(); | |
} | |
// Otherwise the Primary Sidebar is already expanded (open) | |
else { | |
alert( 'Primary Sidebar is already open.' ); | |
} | |
} ); |
“Add a Widget Panel” Event Listener
The last event listener that we have to listen for is the event that will open the Add a Widget “Panel”. To accomplish this, we need to pass a sidebar Control to the availableWidgetsPanel
view’s open()
method. We luck out again because the controls are also stored in a collection and we can reference them when we need to.
Add the following snippet to the ‘init’ function after the snippet above:
// Listen to the "my-open-add-widget-panel" event has been triggered from the Previewer | |
this.preview.bind( 'my-open-add-widget-panel', function() { | |
var primary_sidebar_control = api.control( 'sidebars_widgets[primary-sidebar]' ); // Grab the control from the Customizer settings object | |
alert( '"my-open-add-widget-panel" has been received from the Previewer.' ); | |
// If the Add a Widget Panel is collapsed, open it | |
if ( ! $( 'body' ).hasClass( 'adding-widget' ) ) { | |
// Pass the control to the available widgets panel to give it context | |
api.Widgets.availableWidgetsPanel.open( primary_sidebar_control ); | |
} | |
// Otherwise the Add a Widget Panel is already open | |
else { | |
alert( 'Add a Widget Panel is already open.' ); | |
} | |
} ); |
The code above will open the Add a Widget “Panel” if it is not already open or will pop up an alert()
if it is open.
Bonus: The “Open Everything” Event
Technically you could stop at the last event listener, but to make these examples even more practical, why not have a button that triggers an “open everything” event? This is how our “Add Widget” helper button in Conductor functions.
This final event listener will open the Customizer Sidebar, the Widgets Panel, the Primary Sidebar Section, and the Add a Widget “Panel” all in one click.
Add the following snippet to the ‘init’ function after the snippet above:
// Listen to the "my-open-everything" event has been triggered from the Previewer | |
this.preview.bind( 'my-open-everything', function() { | |
var primary_sidebar_section = wp.customize.section( 'sidebar-widgets-primary-sidebar' ), // Grab the Primary Sidebar from the collection of panels | |
primary_sidebar_control = api.control( 'sidebars_widgets[primary-sidebar]' ); // Grab the control from the Customizer settings object | |
alert( '"my-open-everything" has been received from the Previewer.' ); | |
// First we'll check to see if the Customizer Sidebar is open | |
if ( $( '.wp-full-overlay' ).hasClass( 'collapsed' ) ) { | |
// Trigger a click event on the collapse sidebar element | |
$( '.collapse-sidebar' ).trigger( 'click' ); | |
} | |
// Then we'll check to see if the Primary Sidebar section is open | |
if ( ! primary_sidebar_section.expanded() ) { | |
// Expanding the Primary Sidebar section will also open the Widgets Panel | |
primary_sidebar_section.expand( { | |
// Open immediately (no animation) | |
duration: 0, | |
// We can pass a function to execute upon completion called the completeCallback | |
completeCallback: function() { | |
// Pass the control to the available widgets panel to give it context | |
api.Widgets.availableWidgetsPanel.open( primary_sidebar_control ); | |
} | |
} ); | |
} | |
// Otherwise, if the Add a Widget Panel is collapsed, open it | |
else if ( ! $( 'body' ).hasClass( 'adding-widget' ) ) { | |
// Pass the control to the available widgets panel to give it context | |
api.Widgets.availableWidgetsPanel.open( primary_sidebar_control ); | |
} | |
alert( 'Everything is open!' ); | |
} ); |
Here’s the Complete ‘Active’ Event Callback Function
// When the previewer is active, the "active" event has been triggered (on load) | |
this.preview.bind( 'active', function() { | |
var $body = $( 'body'), $document = $( document ); // Store references to the body and document elements | |
// Append our buttons to the <body> element | |
$body.append( '<button class="previewer-ui-button-1">Trigger my-custom-event</button> <button class="previewer-ui-button-2">Trigger another-custom-event</button>' ); | |
// Listen for events on the first previewer button | |
$document.on( 'touch click', '.previewer-ui-button-1', function( e ) { | |
self.preview.send( 'my-custom-event', window.myCustomData ); | |
} ); | |
// Listen for events on the second previewer button | |
$document.on( 'touch click', '.previewer-ui-button-2', function( e ) { | |
self.preview.send( 'another-custom-event', { 'another-custom-event' : 'data' } ); | |
} ); | |
// Send "my-custom-event" data over to the Customizer | |
self.preview.send( 'my-custom-event', window.myCustomData ); | |
// Append "Open Customizer Sidebar" button to the <body> element | |
$body.append( ' <button class="my-previewer-event-button my-previewer-open-customizer-sidebar" data-customizer-event="my-open-customizer-sidebar">Open Customizer Sidebar</button>' ); | |
// Append "Open Widgets Panel" button to the <body> element | |
$body.append( ' <button class="my-previewer-event-button my-previewer-open-widgets-panel" data-customizer-event="my-open-widgets-panel">Open Widgets Panel</button>' ); | |
// Append "Open Primary Sidebar Section" button to the <body> element | |
$body.append( ' <button class="my-previewer-event-button my-previewer-open-primary-sidebar" data-customizer-event="my-open-primary-sidebar">Open Primary Sidebar Section</button>' ); | |
// Append "Open Add a Widget Panel" button to the <body> element | |
$body.append( ' <button class="my-previewer-event-button my-previewer-open-add-widget-panel" data-customizer-event="my-open-add-widget-panel">Open Add a Widget Panel</button>' ); | |
// Append "Open Everything" button to the <body> element; performs all of the previous button actions simultaneously | |
$body.append( ' <button class="my-previewer-event-button my-previewer-open-everything" data-customizer-event="my-open-everything">Open Everything</button>' ); | |
// Listen for events on the new previewer buttons | |
$document.on( 'touch click', '.my-previewer-event-button', function( e ) { | |
var $this = $( this ); | |
// Send the event that we've specified on the HTML5 data attribute ('data-customizer-event') to the Customizer | |
self.preview.send( $this.attr( 'data-customizer-event' ) ); | |
} ); | |
} ); |
Here’s the Complete Customizer Init Function
// Custom Customizer Previewer class (attached to the Customize API) | |
api.myCustomizerPreviewer = { | |
// Init | |
init: function () { | |
var self = this; // Store a reference to "this" in case callback functions need to reference it | |
// Listen to the "my-custom-event" event has been triggered from the Previewer | |
this.preview.bind( 'my-custom-event', function( data ) { | |
alert( '"my-custom-event" has been received from the Previewer. Check the console for the data.' ); | |
console.log( data ); | |
} ); | |
// Listen to the "another-custom-event" event has been triggered from the Previewer | |
this.preview.bind( 'another-custom-event', function( data ) { | |
alert( '"another-custom-event" has been received from the Previewer. Check the console for the data.' ); | |
console.log( data ); | |
} ); | |
// Listen to the "my-open-customizer-sidebar" event has been triggered from the Previewer | |
this.preview.bind( 'my-open-customizer-sidebar', function() { | |
alert( '"my-open-customizer-sidebar" has been received from the Previewer.' ); | |
// If the "overlay" (Customizer Sidebar) is collapsed, open it | |
if ( $( '.wp-full-overlay' ).hasClass( 'collapsed' ) ) { | |
// Trigger a click event on the collapse sidebar element | |
$( '.collapse-sidebar' ).trigger( 'click' ); | |
} | |
// Otherwise the Customizer Sidebar is already expanded (open) | |
else { | |
alert( 'Customizer Sidebar is already open.' ); | |
} | |
} ); | |
// Listen to the "my-open-widgets-panel" event has been triggered from the Previewer | |
this.preview.bind( 'my-open-widgets-panel', function() { | |
// Grab the widgets panel from the collection of panels | |
var widgets_panel = wp.customize.panel( 'widgets' ); | |
alert( '"my-open-widgets-panel" has been received from the Previewer.' ); | |
// If the Widgets Panel is collapsed, open it | |
if ( ! widgets_panel.expanded() ) { | |
widgets_panel.expand(); | |
} | |
// Otherwise the Widgets Panel is already expanded (open) | |
else { | |
alert( 'Widgets Panel is already open.' ); | |
} | |
} ); | |
// Listen to the "my-open-primary-sidebar" event has been triggered from the Previewer | |
this.preview.bind( 'my-open-primary-sidebar', function() { | |
var primary_sidebar_section = wp.customize.section( 'sidebar-widgets-primary-sidebar' ); // Grab the Primary Sidebar from the collection of panels | |
alert( '"my-open-primary-sidebar" has been received from the Previewer.' ); | |
// If the Primary Sidebar is collapsed, open it | |
if ( ! primary_sidebar_section.expanded() ) { | |
primary_sidebar_section.expand(); | |
} | |
// Otherwise the Primary Sidebar is already expanded (open) | |
else { | |
alert( 'Primary Sidebar is already open.' ); | |
} | |
} ); | |
// Listen to the "my-open-add-widget-panel" event has been triggered from the Previewer | |
this.preview.bind( 'my-open-add-widget-panel', function() { | |
var primary_sidebar_control = api.control( 'sidebars_widgets[primary-sidebar]' ); // Grab the control from the Customizer settings object | |
alert( '"my-open-add-widget-panel" has been received from the Previewer.' ); | |
// If the Add a Widget Panel is collapsed, open it | |
if ( ! $( 'body' ).hasClass( 'adding-widget' ) ) { | |
// Pass the control to the available widgets panel to give it context | |
api.Widgets.availableWidgetsPanel.open( primary_sidebar_control ); | |
} | |
// Otherwise the Add a Widget Panel is already open | |
else { | |
alert( 'Add a Widget Panel is already open.' ); | |
} | |
} ); | |
// Listen to the "my-open-everything" event has been triggered from the Previewer | |
this.preview.bind( 'my-open-everything', function() { | |
var primary_sidebar_section = wp.customize.section( 'sidebar-widgets-primary-sidebar' ), // Grab the Primary Sidebar from the collection of panels | |
primary_sidebar_control = api.control( 'sidebars_widgets[primary-sidebar]' ); // Grab the control from the Customizer settings object | |
alert( '"my-open-everything" has been received from the Previewer.' ); | |
// First we'll check to see if the Customizer Sidebar is open | |
if ( $( '.wp-full-overlay' ).hasClass( 'collapsed' ) ) { | |
// Trigger a click event on the collapse sidebar element | |
$( '.collapse-sidebar' ).trigger( 'click' ); | |
} | |
// Then we'll check to see if the Primary Sidebar section is open | |
if ( ! primary_sidebar_section.expanded() ) { | |
// Expanding the Primary Sidebar section will also open the Widgets Panel | |
primary_sidebar_section.expand( { | |
// Open immediately (no animation) | |
duration: 0, | |
// We can pass a function to execute upon completion called the completeCallback | |
completeCallback: function() { | |
// Pass the control to the available widgets panel to give it context | |
api.Widgets.availableWidgetsPanel.open( primary_sidebar_control ); | |
} | |
} ); | |
} | |
// Otherwise, if the Add a Widget Panel is collapsed, open it | |
else if ( ! $( 'body' ).hasClass( 'adding-widget' ) ) { | |
// Pass the control to the available widgets panel to give it context | |
api.Widgets.availableWidgetsPanel.open( primary_sidebar_control ); | |
} | |
alert( 'Everything is open!' ); | |
} ); | |
} | |
}; |
Wrapping Up
You may notice that some of the buttons in our example may not work correctly at times. For instance, if the Customizer Sidebar is closed, the “Open Add a Widget Panel” button will open the panel, but not the sidebar.
In Conductor we’re making sure that the Customizer Sidebar is open before any of our event functionality is triggered to ensure the user experience makes the most sense. The snippets above could be easily modified to open the Customizer Sidebar before executing. You might want to add logic to your code to check the various “states” of different Customizer elements depending what you’re trying to accomplish.
The core WordPress development team has added some pretty neat enhancements to the Customizer Javascript API recently. These improvements will surely lead to more dynamic Customizer controls and settings in the future, but you can use them in your projects now.
In the next post we’ll talk about optimizing the code that we’ve created so far and making it more efficient. That’s all for now. Thanks for following along!