Been a while since I wrote something. Let’s talk about some of the new stuff available in the Customizer.

Forget about some of part two

First, back in part two, I had a bit about Surfacing the Customizer. That bit is outdated now, WordPress does this for you in later versions. So, yeah, skip that.

Shiny new thing: Panels

Okay, so Panels aren’t that new. They were added in WordPress 4.0. Basically, they’re sliding containers for sections. Having trouble fitting all your settings on the screen? Group the various sections up into Panels. Panels show up as an item in the main list, and when you click the arrow next to them, the whole list glides off screen to show only those sections.

So, now we have four things: Panels, Sections, Controls, and Settings.

  • Panels group Sections together
  • Sections contain Controls
  • Controls are what the user changes
  • Settings define what the Controls change

Creating a panel is easy:

$wp_customize->add_panel( 'some_panel', array(
	'title' => 'Panel 1',
	'description' => 'This is a description of this panel',
	'priority' => 10,
) );

Adding a section to that panel is just as easy:

$wp_customize->add_section( 'themedemo_panel_settings', array(
	'title' => 'More Stuff',
	'priority' => 10,
	'panel'	=> 'some_panel',
) );

All that’s new is a panel setting to tell the section to go into that panel. Simple.

Active Callbacks

One of the problems with the Customizer was that it displayed settings and showed them changing on the site to your right, but the site being displayed is the actual site. Meaning that you can navigate on it. Sometimes, the controls being shown don’t necessarily apply to the actual site that you’re seeing.

Example: If you have a control to change the color of something in the sidebar, but then are looking at a page which has no sidebar, then you have no visual feedback to tell you what the change looks like.

To fix this, “active callbacks” are used.

The active_callback is simply a new parameter that you can pass into Panels, Sections, or Controls. It can contain the name of a function, and that function will be called when the page changes. The function should return true or false (or equivalent) to indicate whether or not the element of the customizer should be shown for that page.

So, if you have a whole Panel that only make sense when the user is looking at Front Page of the site (and not an individual post), then you can do this:

$wp_customize->add_panel( 'front_page_panel', array(
	'title' => 'Front Page Stuff',
	'description' => 'Stuff that you can change about the Front Page',
	'priority' => 10,
	'active_callback' => 'is_front_page',
) );

And voila, when the user is not looking at the front page, the panel simply disappears.

You can use any of the normal WordPress Template Tags for this, or write your own function if you want to be more specific about it.

Edit: As pointed out in the comments, this will not work with ALL of the Template Tags. In particular, is_single won’t work because of the optional parameter it can take. You can define a wrapper for these methods instead, to get the same effect. Example:

function callback_single() { return is_single(); }
...
'active_callback'=>'callback_single'
...

Fun with Active Callbacks

If you do need to write your own callback function, note that the function receives the object in question when it’s called. So, if you attach an active_callback to a Panel, your function will get a argument of the WP_Customize_Panel object in question passed to it. Sections get WP_Customize_Section and such. You can use the information in these to decide whether the panel (or whatever) should be shown for that page.

So, how do we use that object? Well, you can use this to make whether certain controls show or not dependent on the values of other settings. All the various items you can use this on have a link back to the main WP_Customize_Manager. That class has a get_setting function, which you can use to determine what to do.

So, let’s make a control that causes other controls to appear, dependent on a setting.

First, let’s make a simple radio selection control:

$wp_customize->add_setting( 'demo_radio_control', array(
	'default'        => 'a',
) );

$wp_customize->add_control( 'demo_radio_control', array(
    'label'      => 'radio_control',
    'section'    => 'themedemo_panel_settings',
    'settings'   => 'demo_radio_control',
    'type'       => 'radio',
    'choices'    => array(
	'a' => 'Choice A',
	'b' => 'Choice B',
	),
) );

Now, we need to make two other controls, one for each choice. You can actually make as many as you like, we’ll keep it simple.

First, the control for choice A. Let’s make it a simple text control.

$wp_customize->add_setting( 'choice_a_text', array(
	'default' => '',
) );

$wp_customize->add_control( 'choice_a_text', array(
    'label'      => 'Choice A: ',
    'section'    => 'themedemo_panel_settings',
    'type'       => 'text',
    'active_callback' => 'choice_a_callback',
) );

We’ll need that callback function to detect if choice A is selected in the radio control, and return true if it is, and false otherwise. Like so:

function choice_a_callback( $control ) {
	if ( $control->manager->get_setting('demo_radio_control')->value() == 'a' ) {
		return true;
	} else {
		return false;
	}
}

You can simplify that if you like, I spelled it out with an if statement so as to be clear as to what is happening.

panel1

Now for choice B, let’s make it display a color control instead:

$wp_customize->add_setting( 'choice_b_color', array(
	'default' => '#123456',
) );

$wp_customize->add_control( new WP_Customize_Color_Control( $wp_customize, 'choice_b_color', array(
	'label'   => 'Choice B',
	'section' => 'themedemo_panel_settings',
	'settings'   => 'choice_b_color',
	'active_callback' => 'choice_b_callback',
) ) );

And its callback:

function choice_b_callback( $control ) {
	if ( $control->manager->get_setting('demo_radio_control')->value() == 'b' ) {
		return true;
	} else {
		return false;
	}
}

panel1-b
Now, note that the callbacks are very similar. Seems like repeated code, doesn’t it? Well, it is, but remember that the $control here is the whole WP_Customize_Control object. We can use the same callback and simply check which control is calling it here instead.

function choice_callback( $control ) {
	$radio_setting = $control->manager->get_setting('demo_radio_control')->value();
	$control_id = $control->id;
	
	if ( $control_id == 'choice_a_text'  && $radio_setting == 'a' ) return true;
	if ( $control_id == 'choice_b_color' && $radio_setting == 'b' ) return true;
	
	return false;
}

So, instead of using two different callbacks, we just point our controls to this callback, which figures out what should show up for which setting. I’m sure you can simplify this further, depending on your particular needs.

One more thing: Customizing the Customizer

Not everybody likes the style of the Customizer. Maybe it clashes with your theme. Maybe you just want to tweak it a bit. Maybe you dislike that gray background color, and a more soothing blue would go better for your theme.

add_action( 'customize_controls_enqueue_scripts', 'themedemo_customizer_style');
function themedemo_customizer_style() {
	wp_add_inline_style( 'customize-controls', '.wp-full-overlay-sidebar { background: #abcdef }');
}

Or maybe you don’t think the Customizer area is wide enough… be careful with this one though, consider mobile users as well.

add_action( 'customize_controls_enqueue_scripts', 'themedemo_customizer_style');
function themedemo_customizer_style() {
	wp_add_inline_style( 'customize-controls', '.wp-full-overlay-sidebar { width: 400px } .wp-full-overlay.expanded { margin-left: 400px } ');
}

You can enqueue whole extra CSS files instead, if you like. Or, if you have special needs for javascript in some of your controls, and there’s libraries necessary to implement them, then you can enqueue those libraries here as well.

 

Shortlink:

56 Comments

  1. Ah…more cool things to play with. Now I will never get my themes finished, lol. Thanks Otto!
    Anyway, I remember you replied to one of my comments at WPTavern in relation to the Customizer and said to let you know if there’s anything new I can suggest…well, here’s something I’ve been trying to find. How to change the radio inputs control to be images. For example, let’s say I have 8 layouts for the blog, each one is a radio button array.

  2. I love you man!!! This is great stuff. This is exactly what we need right now!!! This will make WP-Forge even better amigo. Again thank you, I appreciate the post and all the work you do.

  3. Very informative post Otto!! I have been relying on a dedicated Theme Options page for my themes because WP Customizer always seemed counter-intuitive. One of my clients even termed it “tedious and confusing”. However, I have been reading more and more positive stuff about WP Customizer lately. I think now I am confident enough to use Customizer in my upcoming projects.

  4. Amazing tutorial! I’ve just added this post to Codex. Hope it’s useful for everyone.

  5. Even I expected of you to post what’s new on the subject. Very good explanations

  6. Thanks for writing this up! Great to see some more examples of what can be done with the latest iterations on the Customizer. By the way, Nick Halsey (celloexpressions) just contributed an amazingly thorough writeup on the Customizer API for the Theme Handbook: https://developer.wordpress.org/themes/advanced-topics/customizer-api/

  7. […] “Otto” Wood, who has written several customizer articles over the years, wrote a “What’s new with the Customizer” tutorial that explores some of its newer features in depth, including panels, active […]

  8. Hi Otto.
    Thanks for your update of this series to the customizer.
    I play currently with him, also for customization of front end topics, but not directly on the theme.
    I have the problem, that I can’t change the url to the page, there I will customize via the Customizer.
    I had play with add_query_arg(), but no result, that works. Also it is possible to parse via admin_menu hook the globals $submenu, $menu and change the url, like get_admin_url() . 'customize.php?url=' . wp_login_url() to customize the login url. But is not really clear and also in this topic after close the customizer I was logged out of the install.
    Maybe you have a hint for me to create a url on click on a menu item in the Design Menu item?
    Thanks a lot for the time.

  9. Thanks for writing this up! I’ve recently been tasked with transitioning an older theme options implementation to using the Customizer API. Your posts have been extremely helpful in ramping up my knowledge — Thank You!

  10. Hey Otto, just wanted to let you know that the images you used in this tut stopped showing for some reason.

  11. Hi Otto,

    Quick question. I know that the default for the add_setting is for the customizer preview, not for the get_theme_mod, how do I set a default for get_theme_mod if its used in a if statement, for example in content.php For example:

    	<?php if ( get_theme_mod( 'some_setting_name' ) == 'yes' ) : ?>
    			//show something here
    	<?php endif; // end if ?>
    

    The issue I am having is this: When someone activates my theme for the first time, in customizer view whatever is supposed to be shown by some_setting_name is being shown, however, if they look at the front of the site, nothing is being displayed where some_setting_name is suppose to display.

    Could you explain how to do that if you have the time. Thank you.

  12. Hey Otto,

    What about default customizer sections, such as custom background color, that appear when you do add_theme_support('custom_background'); ? Any plans to support panel that they shoud appear?

    Thanks
    d.

    • @dd, You can assign any section, default or not, to any panel you wish. This is very easy to do. I just created a panel and then I assigned it to that panel. Here is what I did, first I needed to find out what section the background color was initially assigned to. I did that by looking at this https://github.com/WordPress/WordPress/blob/master/wp-includes/class-wp-customize-manager.php, its located on lines 1395 through 1398. It states it is assigned to the colors section.

      So then I created a panel like so:

        $wp_customize->add_panel( 'wpforge_colors', array(// Color Panel
            'priority'       => 4,
            'capability'     => 'edit_theme_options',
            'title'          => __('Colors Section', 'wp-forge'),
            'description'    => __('Deals with the colors of your theme.', 'wp-forge'),
        ));
      

      then I assigned the colors section to that new panel like so:

      $wp_customize->get_section('colors')->panel = 'wpforge_colors'; // Add to Colors Panel
      

      I created different color sections for my theme, i.e. one for sidebar colors, one for footer colors and so on and then assigned those sections to the Colors Section panel by adding this to each section:

      'panel' => 'wpforge_colors',
      

      I am now getting into adding active_callback – which is very very cool.

  13. Hi Otto,

    I try to undestand a bit better the active_callback paraemeter. As you stated we can use any WordPress Template Tag but when I try to use something different from is_front_page but nothing seems to work, neither a simple one as is_single.

    Can anyone help me up with this?

    • That part may be incorrect. Not all template tags will work, because some of them accept parameters. The is_single function actually takes a post as the parameter, and so it can’t understand the control object being passed to the active_callback. You would need to write a wrapper function so as to have it ignore the parameter. Like `function callback_single() { return is_single(); }`, then pass that function to the active_callback instead.

  14. Hi Otto,

    Is there a way to use active callbacks for sections.

    For example, let’s say I have a section called Menu select. In this section are two options: menu A and Menu B. Each menu is different and has different settings.

    So if someone selects menu A, a section called Menu A Settings would appear, if they chose menu B, a section called Menu B Settings would appear.

    Is this possible to do?

  15. Great article, I didn’t know the active callback functions received the control objects. I’ll have to do some refactoring to my themes.

    But I do have a question, is it possible for for the active_callback parameter to receive a class? Similar to how the add_action function receives classes e.g. “add_action( ‘save_post’, array( $my_class, ‘save_posts_hook’ ) );”?

  16. In the new Customizer I can see the Colors section, right under the Site Identity. And it’s empty. I already created a ‘Theme Colors’ section that uses the WP_Customize_Color_Control, and it works fine. Can I somehow use this default Colors section that appeared, or can I at least remove it somehow?

    I like the look of the new Customizer, it’s really fast :) Also great tips in the article, I’ll be sure to use some :)

  17. Fantastic article!! I just tried active callbacks to show/hide fields. Do you have guys any soluttion to show/hide using JS with transport => ‘postMessage’.

    I am testing and it seems you cannot manipulate self customizer HTML from JS API. I am trying something like:

    wp.customize( 'font_sources_select', function( value ) {
        value.bind( function( to ) {
            $( 'li#customize-control-google_mainfont, #customize-control-google_secondfont' ).css( {
                        'display': 'none',
                    } );
    } );
    

    Thanks!!

    • Self solved!! Just need to change the action to make js file able to deal with self customizer themes. In case it helps:

      function live_preview() {
        wp_enqueue_script( 'customizer-js', get_template_directory_uri() . '/assets/js/customizer.js', array(  'jquery', 'customize-preview' ), '100', true);
      }
      // Just reflect on theme code
      add_action( 'customize_preview_init' , 'live_preview' );
      // Active js inside customizer controls
      add_action( 'customize_controls_enqueue_scripts', 'live_preview' ); 
      
  18. Loving the ability to conditionally show/hide a setting based on the value of a different setting, but I notice a problem and I’m wondering if there is a work-around?

    In my scenario, setting 1 is a radio button list and setting 2 (a checkbox) should only be shown if setting 1 is ‘large’. I use an “active_callback” to check if setting 2 should be shown, which works fine when I use “‘transport’ => ‘refresh'” for setting 1, but it fails when I use “‘transport’ => ‘postMessage'”.

    I can see the logic as to why this would fail, so I’m looking for a way around it, such as calling the relevant “active_callback” through the “wp.customize” object, but I cannot find a way.

    I’ve also started a SO question on this subject – http://wordpress.stackexchange.com/questions/211779/customiser-active-callback-not-working-on-control-with-postmessage-transport

    Is this something you have investigated in the past?

    • I should have added that I have tried a couple of things, but they failed…

      Change the control visibility via “wp.customize.settings”


      wp.customize('archive_link_tile_size', function(value){

      value.bind(function(newval){
      PreviewLinkTileSize(newval);
      if(newval !== 'large')
      wp.customize.settings.archive_show_more_link = false;
      });

      });

      Trigger a change to the ‘archive_show_more_link’ control


      wp.customize('archive_link_tile_size', function(value){

      value.bind(function(newval){
      PreviewLinkTileSize(newval);
      wp.customize.trigger('archive_show_more_link');
      });

      });

    • You can’t use active callbacks with postMessage, because the whole point of postMessage is to stop the refresh process. Since the PHP only executes when the refresh happens, then the two are fundamentally at odds.

      Don’t use postMessage with active callbacks. It won’t work, for obvious reasons. There is no workaround. Just don’t even try it.

      • Well that just makes me sad :(

        I figured that may be the case, it’s a shame you can’t trigger the callback via JS though; it would be a neat feature for use-cases like mine.

      • You can do it in JS. You just have to change the control’s active state via control.active.set( false ) (or true) depending on whether it is being made active or not. In other words, you can implement the same logic for the PHP active_callback in JS, like via the control’s ready method. I can share a working example if you like.

  19. Hi Otto,

    Just a quick question and I am not sure if this was covered above or not.

    I have a setting in the customizer that allows the user to set an element to display either to the left or to the right. The default is Left. As of now the user can only see the change on refresh and I would like to show the change as soon as the user makes the selection.

    I added everything necessary including ‘transport’ => ‘postMessage’ and so on, but when I try it nothing is happening. There is no screen refresh when Right is selected and the div does not switch to the right.

    Any idea how I can get this to work?

  20. Hi Otto,
    Can you help me to customize the header section of the website? Is their any standardization?

  21. Hi Otto,

    Just a quick question and I am not sure if this was covered above or not.

    I have a setting in the customizer that allows the user to set an element to display either to the left or to the right. The default is Left. As of now the user can only see the change on refresh and I would like to show the change as soon as the user makes the selection.

    I added everything necessary including ‘transport’ => ‘postMessage’ and so on, but when I try it nothing is happening. There is no screen refresh when Right is selected and the div does not switch to the right.

    Any idea how I can get this to work?

  22. Thanks Otto for this walkthrough! I have used this as a reference starting point in a build i am working on. Thanks!

    Reply back if you want me to buy a beer or coffee! cheers!

  23. Hi Otto
    i’m trying to add a section/panel inside the title_tagline section/panel – but it will not show- only displays in “root” of customizer – is it not possible to add a section/panel inside the defaults sections/panels?
    I’ve tried using add_section and both: ‘panel’=>’title_tagline’ and ‘section’=>’title_tagline’ without luck
    controls can be added to title_tagline just fine but not sections/panels

  24. Hi Otto,

    I try to use something different from is_front_page but nothing seems to work, neither a simple one as is_single.
    Can anyone help me up with this?

    thanks

  25. […] Source: What’s new with the Customizer » Otto on WordPress […]

  26. […] What’s new with the Customizer » Otto on WordPress – Useful reference for active_callback. […]

Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Need to post PHP code? Wrap it in [php] and [/php] tags.