Navigating to a URL in the Customizer Preview when a Section is Expanded

Authored by

On Otto’s What’s new with the Customizer post there’s a long-running comments thread in which a good question was raised by Jascha:

with the active callback you can show a section or panel depending on the URL you are looking at. But is it possible the other way around? Showing a different URL depending on the section you have currently selected. Have been searching for hours for a way to do it. I know that you can change the previewURL with wp.customize.previewer.previewUrl but I wasn’t able to find how to bind this to the currently selected section/panel.

The code required to do it is pretty straightforward. Here’s a useful example where the preview URL is changed to the homepage when you expand the “Static Front Page” section:

(function ( api ) {
	api.section( 'static_front_page', function( section ) {
		section.expanded.bind( function( isExpanded ) {
			var url;
			if ( isExpanded ) {
				url = api.settings.url.home;
				api.previewer.previewUrl.set( url );
			}
		} );
	} );
} ( wp.customize ) );

(This could be an alternate implementation for what was done in #30677.) Here’s a breakdown for what is happening in this code snippet:

  1. Wrap code in an IIFE (optional):
    (function ( api ) { … } ( wp.customize ) );
  2. Register a deferred callback to run when the Static Front Page section exists:
    api.section( 'static_front_page', function( section ) { … } );
  3. Register a listener for when the section’s expanded state changes:
    section.expanded.bind( function( isExpanded ) { … } );
  4. Set the preview URL to the homepage when expanded state is true:
    if ( isExpanded ) { api.previewer.previewUrl.set( api.settings.url.home ); }

This JS should be enqueued in the customize_controls_enqueue_scripts action with customize-controls as its dependency.

This concept of navigating to a URL in the preview when a section is expanded is something we’ve been exploring in the Customize Posts plugin as well. In Customize Posts, each post type gets its own panel, and these panels contain sections that correspond to posts of that type (i.e. pages or posts of any given custom post type); the post sections are added dynamically to the panel when posts are present in the preview. When you create a new post in the Customizer via the post type panel’s “Add New Post” button it creates a new auto-draft post; it then creates a Customizer section for it, expands the section, and then the post preview URL is navigated to in the preview so that you can see changes to the post as you fill out its fields. See relevant code.

In addition to dynamically (passively) adding and showing Customizer sections for posts that currently appear in the preview, we’re also exploring a way for Customize Posts to allow you to be more assertive about accessing the desired post. In a recent pull request there is a new Select2 dropdown that allows you to search through all posts of a given type, and then upon selection, add a Customizer section for that post (if it hasn’t already been added), and then both expand the section and navigate to the preview URL for that post in the Customizer preview if the post is not currently visible in the preview (and if the post type itself is public). See relevant code.

In summary, contextual Customizer panels and sections were added in #29758. This allowed you to add an active_callback to toggle the visibility of a panel or section based on the URL being previewed. If you had a Customizer section for managing the settings for a given area of your site, this would require that you first navigate to that area of the site in order for the section to be displayed. This isn’t optimal for discoverability in the Customizer, and so it seems useful to sometimes switch things around: always have the section visible (no active_callback) and then when that section is expanded automatically navigate to the relevant area of the site in the preview.

Addendum: Return Preview to Previous URL upon Section Collapse

Something else which may be useful beyond navigating to a URL when a section expands is to also return to the previous URL when that section collapses, as Andrew Taylor commented below. Doing this is straightforward as we just have to keep track of the preview URL before it was changed, and then restore the preview URL to this previous URL when the section is collapsed. As a further refinement, we can prevent this preview URL restoration if the user further navigates after the section is expanded, as it might be unexpected for the preview URL to change back in this case. Note that with #28536 (Add browser history and deep linking for navigation in Customizer preview) this may eventually be able to be simplified to doing history.back(). Here is amended code sample to restore the previous URL upon section collapse:

(function ( api ) {
	api.section( 'static_front_page', function( section ) {
		var previousUrl, clearPreviousUrl, previewUrlValue;
		previewUrlValue = api.previewer.previewUrl;
		clearPreviousUrl = function() {
			previousUrl = null;
		};

		section.expanded.bind( function( isExpanded ) {
			var url;
			if ( isExpanded ) {
				url = api.settings.url.home;
				previousUrl = previewUrlValue.get();
				previewUrlValue.set( url );
				previewUrlValue.bind( clearPreviousUrl );
			} else {
				previewUrlValue.unbind( clearPreviousUrl );
				if ( previousUrl ) {
					previewUrlValue.set( previousUrl );
				}
			}
		} );
	} );
} ( wp.customize ) );

13 thoughts on “Navigating to a URL in the Customizer Preview when a Section is Expanded”

  1. Hey Weston,
    I think it’s good to note that in your example isExpanded will return false when the section is collapsed.

    If you navigate to a URL when the section is expanded it might be nice to navigate back to the previous URL when it the sections closed.

    1. @andrew Good point. This is similar to something that was recently added to Customize Posts, except it is part of a “breadcrumbs” integration where a control can be returned to when it expands a section an entirely different panel. The use case here is adding create and edit buttons to the dropdown-pages control. So you can click to edit the selected Page on Front, it then opens the section that contains the controls for that page, and then when that section is collapsed the section for Static Front Page is re-expanded with that control focused as opposed to going to the Pages panel.

  2. Hey Weston,

    Thanks for the post on this, it’s something I’ve been wanting to know if it was possible for awhile.

    Is it possible to show the wp-admin/wp-login.php with this function though? I’m trying to use it so a user can customize the login screen etc however when I do the iframe doesn’t seem to get replaced it actually creates a duplicate which is overlayed by the first.

    You should be able to see what I mean here – http://imgur.com/a/8CWI5

    However when you use another URL you can see that the customize-preview-0 gets removed.

    Many thanks

      1. Thanks for your time and explanation Weston, much appreciated. I can’t believe after all my searching I never came across that post!

        All the best,

        Adam

  3. Hey Weston!

    First thing, I’m a fan of WordPress Customizer, I’m really excited to typing to someone who makes WordPress Customizer!

    I found your post while researching more about context-aware previews. What I’m trying to do here is; if the “Post Settings” section/panel is expanded, it should redirect to a random post/the first post on the blog. Same with the “Page Settings area, Same for “Category Settings”, “Tags Settings” etc.

    Is it doable? What are your thoughts on this?

    Thanks for the helpful stuff Weston, have a good one 🙂

  4. Hi, just found your site, and this is exactly what I am looking for.

    But I cannot make it work, I can see that when clicking a section I specified the Customizer starts loading, but it doesn’t change the page.
    Is this the correct place to set the URL?

    url = ( api.settings.url.home + ‘/check-in’ );

    What I am trying to do is to get the home URL + /check-in, so it looks like this: https://example.com/check-in

    Am I doing it wrong?

  5. I just tested some more, and I can see that in the console it gets the page. And that it should work. But the old page is still what is visible. Why is that?

  6. Any way to scroll to an anchor?
    ex.
    url = /sample-page/#buttons

    The use case here would be to guide the user down to relevant sections of the page as they click through the sections in the customizer.

    1. Take a look at the Customizer JS in the Twenty Seventeen theme. There is some logic in there for scrolling to home page sections. Otherwise, anchor links in the Customizer preview aren’t supported.

  7. i am stuck with changing preview url to my plugins custom page.
    can you help cause nothing is working.
    i tried with api.previewer
    sending with customize.php?url=
    it always opens index page.

    i also tried changing source by script but it refreshes back to index when any setting alters.

    1. Also, for more information, I am trying to do that with Multisite.
      I am trying to make a plugin which involves customizer to change styles of wp-login.php
      On a single site, I just set the url of wp-login.php with customize.php, and the login page opens even when I am logged in. Settings worked flawlessly, but doing the same in a multisite iframe opens the dashboard, not the login page.
      Can you help me with a good approach with multisite to display wp-login.php in an iframe even when I am logged in as an admin user, or some different approach in which I would be able to load a duplicate custom login page in an iframe?

      Thanks hoping for reply.

Leave a Reply to Joakim Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.