Make WordPress Core

Opened 12 years ago

Closed 8 years ago

#23225 closed defect (bug) (fixed)

Customizer is Incompatible with jQuery UI Tabs.

Reported by: mfields's profile mfields Owned by: westonruter's profile westonruter
Milestone: 4.7 Priority: normal
Severity: normal Version: 3.4
Component: Customize Keywords: has-patch needs-testing
Focuses: Cc:


Steps to reproduce:

  1. Install the attached mfields-test-jquery-ui-tabs.php plugin.
  2. Open the Chrome console.
  3. Open the customizer and watch the console.

At this point you should see that a slew of XHR requests are triggered. If you reach a certain type of javascript error then the XHR requests will stop and the preview frame in the customizer will go blank.

It seems like jQuery UI Tabs and the customizer are currently incompatible. This will affect any theme or plugin that is using this library including the popular Genesis Tabs plugin.

Attachments (3)

mfields-test-jquery-ui-tabs.php (3.5 KB) - added by mfields 12 years ago.
class-wp-customize-manager.php.patch (1.7 KB) - added by deltafactory 11 years ago.
Customizer patch remaps A tags that start with # to behave as intended.
23225.2.patch (1.8 KB) - added by westonruter 10 years ago.

Download all attachments as: .zip

Change History (48)

#1 @SergeyBiryukov
12 years ago

  • Component changed from General to Appearance

#2 @pauldewouters
12 years ago

  • Cc pauldewouters@… added

#3 @daveshine
12 years ago

  • Cc daveshine added

#4 @wpsmith
12 years ago

  • Cc t@… added

#5 @philiparthurmoore
11 years ago

  • Cc philip@… added

#6 @SergeyBiryukov
11 years ago

  • Version changed from trunk to 3.4

#8 @sennza
11 years ago

  • Cc bronson@… added

#9 @kwight
11 years ago

  • Cc kwight@… added

#10 @jjeaton
11 years ago

  • Cc jjeaton added

#11 @deltafactory
11 years ago

This is related to a hotly debated jQuery "bug" related to the use of the <base> tag, which done by the Customizer. See their ticket here:

Specifically, the result of jQuery UI Tab's isLocal() on hash-only urls (e.g. href="#my-tab-1") unexpectedly returns false. This triggers an ajax call to pull in the tab's content. In the Customizer's case, that's the Customizer page itself (not pretty.)

Possible fixes:

The jQuery dev team recommends the use of absolute URLs.

I will suggest a simple check to see if URL to the left of the hash is an empty string which should be "local" by definition, though I forsee poor reception.

One commenter on the ticket has detailed the problem and a few suggestions here His third suggestion is a hacky work-around but may be the most universal fix for the Customizer barring a solution by jQuery.

#12 @helen
11 years ago

#24032 was marked as a duplicate.

11 years ago

Customizer patch remaps A tags that start with # to behave as intended.

#13 @deltafactory
11 years ago

The preceding patch makes two changes:

  1. It adds an ID to the <base> tag injected by the Customizer. It's a shortcut for the rest of the code but future customizer enhancements may find it useful as well.
  1. Injects an inline script in the footer that prepends all links that begin with # with the absolute URL of the Customizer.

This has the benefit of restoring expected behavior of hash-links which had been broken in any way while the Customizer was in use.

I'd prefer to avoid script injection but I think it was the easiest method to ensure remapping before jQuery's ready event fired. The mfields plugin test case showed proper behavior but I'm looking for more feedback about the approach and cross-browser testing.

#14 @deltafactory
11 years ago

  • Keywords reporter-feedback needs-testing added; needs-patch removed

#15 @SergeyBiryukov
11 years ago

#26005 was marked as a duplicate.

#16 @dreamwhisper
11 years ago

The patch provided by @deltafactory appears to resolve the issue with jQuery UI Tabs.

#17 @helen
11 years ago

Just reading the patch - will this approach work for any other page beyond the site home, since the base URL is home_url?

#18 @helen
11 years ago

  • Keywords has-patch added; reporter-feedback removed

#19 @deltafactory
11 years ago

@helen, the patch works by tricking jQuery UI's isLocal() function into thinking that a # link is local by matching the domain of the hash URL to the page's. So it should work anywhere that the domain for the Customizer (derived from admin_url()?) is the same as home_url().

I believe it will break regular hash URLs, though no more than the initial use of the <base> tag.

I don't recall the exact code, but if there's another way to determine the interior page's URL then I'd be happy to use that. Between the method used by the Customizer to inject the HTML into the iframe, and cross-frame security, there may be limitations though I'd have to take a closer look.

#20 @deltafactory
10 years ago

Not sure if this is still relevant given ongoing improvements to Customizer, but would be interested in feedback/inclusion in 4.1.

#21 @westonruter
10 years ago

  • Milestone changed from Awaiting Review to 4.1

In class-wp-customize-manager.php.patch, I'm seeing that document.location.href is not returning the URL of the Customizer preview even though a base[href] is being set (e.g. I'm seeing instead of This is probably due to the fact that the preview iframe is getting populated dynamically via adocument.write() call and not by actually being navigated to, so the preview window is inheriting the parent window's document.location.href.

So It seems way for preview to determine its own URL is to instead look at the base DOM element's href property. In 23225.2.patch I've made this change. It's also now using jQuery to update the link href attributes, and it is outputting a base[href] containing the REQUEST_URI not just the home, to address your concern @helen.

Nevertheless, the Customizer hasn't been taking into account that a theme may be outputting a base element of its own. Also, if the Customizer preview dynamically adds new # links they're not going to be rewritten to have the right base.

Patches for this ticket are added to a PR on GitHub:

As an aside, all of this leads me to think that the current method for populating the Customizer preview iframe introduces fundamental limitations, and some things that just aren't going to work in the way it is currently designed. It would be better if we could use the iframe in a more traditional way, setting the src attribute to the URL being previewed (doing a GET request) and not doing document.write() on an about:blank iframe (writing the response to an Ajax POST); this would require an alternative back-channel for passing the settings data to the preview.

#22 follow-up: @deltafactory
10 years ago

The behavior you were seeing from document.location.href is "correct" (the source of the entire bug/dispute with jQuery UI) and directly related to the issue my patch resolved.

I agree with your concerns about the use of base, document.write() etc. though I intentionally avoided using jQuery as a dependency for the same reason.

#23 in reply to: ↑ 22 @westonruter
10 years ago

Replying to deltafactory:

The behavior you were seeing from document.location.href is "correct" (the source of the entire bug/dispute with jQuery UI) and directly related to the issue my patch resolved.

I'm not sure I understand. So it was intended that document.location.href should be …/wp-admin/customize.php and not the actual base URL for the URL being previewed?

#24 @deltafactory
10 years ago

Based on jQuery UI's isLocal() declaration, now refactored to _isLocal() here: ... they define a local URL as one that is the same as location.href, up to the hash.

It's this definition of local that is the root of the jQuery UI tab incompatibility and the resulting remote request causes the recursive glitches.

Version 0, edited 10 years ago by deltafactory (next)

#25 @westonruter
10 years ago

Humm, then I'm not sure patching Core to supply a wp-admin URL as the base URL for links inside the preview is desirable. It seems like this hack to fix one script's problem could cause unintended side-effects elsewhere in other scripts.

#26 @deltafactory
10 years ago

It's not the most elegant solution but I think it fixes more than it breaks. To your point, as I said once upon a time in comment 19.

I believe it will break regular hash URLs, though no more than the initial use of the <base> tag.

Until such time as the Customizer iframe implementation is replaced, it seems like there are a number of issues that can't be fixed.

Since the ticket was marked has-patch, needs-testing with some positive feedback, I'd be happy to receive feedback on failed tests in lieu of a reimplementation.

This ticket was mentioned in IRC in #wordpress-dev by celloexpressions. View the logs.

10 years ago

#28 @westonruter
10 years ago

I've opened #30028 to investigate the alternative strategy for managing the Customizer iframe.

This ticket was mentioned in Slack in #core by ocean90. View the logs.

10 years ago

#30 @ocean90
10 years ago

  • Milestone changed from 4.1 to Future Release

The latest patch doesn't fix the issue. Punt.

#31 @deltafactory
10 years ago

... The latest patch didn't fix the issue, but the next-to-last patch did. Is #30028 the future of the Customizer? Did my patch break anything? I'm still not sure why it was superseded by a patch that didn't quite fix the problem.

To westonruter's main concern that it will break non-jQuery tabs, I believe the behavior intended by use "#idref" urls in other situations is retained or improved by the technique implemented here.

While it's definitely too late for inclusion into 4.1, I would still like feedback. In the event that a reimplementation of the Customizer previous mechanism is in the works, I would like to be part of that discussion as well.

#32 @westonruter
10 years ago

deltafactory is right. The penultimate patch is the one that would have fixed the problem, although it was a workaround for a more fundamental issue which #30028 would address. Nothing has been done on that front yet, however, though I'll be sure to post any updates to the ticket when I get a chance to work on a proof of concept. I probably won't be able to until Christmas break, however.

#33 @westonruter
10 years ago

I've been doing some work on #30028 over the holidays. Still a work in progress.

This ticket was mentioned in Slack in #core-customize by westonruter. View the logs.

10 years ago

This ticket was mentioned in Slack in #core-customize by westonruter. View the logs.

9 years ago

#36 @cliffpaulick
9 years ago

Using WP 4.3...

I have a shortcode that implements (does not use jQuery).

Even when I make the HREFs absolute to the current page's permalink (e.g. #panel-7 -->, the WP Customizer preview gets reloaded instead of switching to the tab I clicked within the current page view.

Yeah, at least the tab link stays on the current page instead of #panel-7 going to, but the user's click to switch tabs inside the WP Customizer preview still does not work as expected.

This ticket was mentioned in Slack in #core-customize by westonruter. View the logs.

8 years ago

#38 @westonruter
8 years ago

  • Milestone changed from Future Release to 4.6
  • Owner set to westonruter
  • Status changed from new to accepted

#39 @westonruter
8 years ago

  • Milestone changed from 4.6 to Future Release

Punting due to transactions being too large to finish patch and test in time for 4.6.

#40 @Unyson
8 years ago

Similar problem, but when with a newer version of jquery.ui.tabs it works fine

This ticket was mentioned in Slack in #core-customize by celloexpressions. View the logs.

8 years ago

#42 @celloexpressions
8 years ago

  • Keywords needs-patch added; needs-testing has-patch removed

This is still pending transactions and #30028.

#43 @westonruter
8 years ago

  • Keywords has-patch needs-testing added; needs-patch removed
  • Milestone changed from Future Release to 4.7

This has been implemented in the patch for #30937.

Please test: grunt patch:

This ticket was mentioned in Slack in #core by westonruter. View the logs.

8 years ago

#45 @westonruter
8 years ago

  • Resolution set to fixed
  • Status changed from accepted to closed

In 38810:

Customize: Implement customized state persistence with changesets.

Includes infrastructure developed in the Customize Snapshots feature plugin.


Props westonruter, valendesigns, utkarshpatel, stubgo, lgedeon, ocean90, ryankienstra, mihai2u, dlh, aaroncampbell, jonathanbardo, jorbin.
See #28721.
See #31089.
Fixes #30937.
Fixes #31517.
Fixes #30028.
Fixes #23225.
Fixes #34142.
Fixes #36485.

Note: See TracTickets for help on using tickets.