jQuery 3.0 Beta Released

January 15th, 2016

The time has come. On this day, the 10th anniversary of jQuery, jQuery 3.0 has reached beta status. Last week, we announced the last minor releases to the 1.x and 2.x branches. Those branches will continue to receive patches for a limited time (i.e. only major regressions or bugs); jQuery 3.0 is the future. If you need IE6-8 support, you can continue to use the latest 1.12 release.

The Death of jQuery Compat


If you read the jQuery 3.0 alpha blog post, you might remember that we announced something we called “jQuery Compat”. You can forget that. On January 12, Microsoft dropped support for IE8, IE9, and IE10. We’re not going to go that far just yet, but we are dropping support for IE8. And with IE8, so goes jQuery Compat, gone before we even released a final version. There will only be one jQuery from now on!

Despite the 3.0 version number, we anticipate that these releases shouldn’t be too much trouble when it comes to upgrading existing code. Yes, there are a few “breaking changes” that justified the major version bump, but we’re hopeful the breakage doesn’t actually affect that many people. The jQuery Migrate 3.0 plugin, when released, will help you to identify compatibility issues in your code as well. Your feedback on the changes will help us greatly, so please try it out on your existing code and plugins!

You can get the files from the jQuery CDN, or link to them directly:

You can also get the beta version from npm:

npm install jquery@3.0.0-beta1

Major changes

Below are just the highlights of the major new features, improvements, and bug fixes in these releases. A complete list of changes is available on our GitHub bug tracker.

.show() and .hide() methods

In jQuery 3.0 alpha, we experimented with the idea of treating these methods like an inline-display-none-remover (.show()) and inline-display-none-adder (.hide()). This had the advantage of simplifying these methods greatly and improving performance (it required much fewer calculations). However, this proved to be problematic for our users. Removing inline display:none did not always show the element (if the element was hidden from the stylesheet, for example), and that is far too common. We realized we couldn’t provide a simple way for jQuery plugins, especially, to ensure that an element was shown.

We’ve since reverted that change, and the changes that we’ve kept for the show and hide methods should have much less of an impact on your code. In fact, even with the reversion, we’ve greatly improved performance for the case of hiding many elements.

Special case with `.data()` names

We have updated our .data() implementation to closer match the HTML5 dataset specification. All keys are now converted from kebab-case to camelCase, regardless of access method, and digits no longer participate in the conversion. For example, we will no longer differentiate between “foo-bar” and “fooBar”, but will differentiate between “foo-42” and “foo42”. These changes will mainly come into play when retrieving all data by calling .data() with no arguments, or when trying to access the data using a converted key (.data(“foo42”)) instead of the original (.data(“foo-42”)).

jQuery.Deferred is now Promises/A+ compatible

jQuery.Deferred objects have been updated for compatibility with Promises/A+ and ES2015 Promises, verified with the Promises/A+ Compliance Test Suite. This meant we need some major changes to the .then() method:

  • An exception thrown in a .then() callback now becomes a rejection value. Previously, exceptions bubbled all the way up, aborting callback execution and irreversibly locking both the parent and child Deferred objects.
  • The resolution state of a Deferred created by .then() is now controlled by its callbacks—exceptions become rejection values and non-thenable returns become fulfillment values. Previously, returns from rejection handlers became rejection values.
  • Callbacks are always invoked asynchronously. Previously, they would be called immediately upon binding or resolution, whichever came last.
  • Progress callbacks can no longer resolve Deferred objects to which they are bound.

Consider the following, in which a parent Deferred is rejected and a child callback generates an exception:

var parent = jQuery.Deferred();
var child = parent.then( null, function() {
  return "bar";
var callback = function( state ) {
  return function( value ) {
    console.log( state, value );
    throw new Error( "baz" );
var grandchildren = [
  child.then( callback( "fulfilled" ), callback( "rejected" ) ),
  child.then( callback( "fulfilled" ), callback( "rejected" ) )
parent.reject( "foo" );
console.log( "parent resolved" );

As of jQuery 3.0, this will log “parent resolved” before invoking any callback, each child callback will then log “fulfilled bar”, and the grandchildren will be rejected with Error “baz”. In previous versions, this would log “rejected bar” (the child Deferred having been rejected instead of fulfilled) once and then immediately terminate with uncaught Error “baz” (“parent resolved” not being logged and the grandchildren remaining unresolved).

While caught exceptions had advantages for in-browser debugging, it is far more declarative (i.e. explicit) to handle them with rejection callbacks. Keep in mind that this places the responsibility on you to always add at least one rejection callback when working with promises. Otherwise, any errors will go unnoticed.

Legacy behavior can be recovered by replacing use of .then() with the now-deprecated .pipe() method (which has an identical signature).

We’ve also built a plugin to help make debugging Promises/A+ compatible Deferreds. If you figure out that there’s some phantom error getting eaten, check out the jQuery Deferred Reporter Plugin.

jQuery.when has also been updated to accept any thenable object, which includes native Promise objects.

Added .catch() to Deferreds

The catch() method was added to promise objects as an alias for .then(null, fn).

Removed special-case Deferred methods in jQuery.ajax

jqXHR object is a Promise, but also has extra methods like .abort() so that you can stop a request after it has been made.

As users increasingly embrace the Promise pattern for asynchronous work like AJAX, the idea of having special cases for the Promise returned by jQuery.ajax is an increasingly bad idea.

success, error, complete
done, fail, always

Note that this does not have any impact at all on the callbacks of the same name, which continue to exist and are not deprecated. This only affects the Promise methods!

Error cases don’t silently fail

Perhaps in a profound moment you’ve wondered, “What is the offset of a window?” Then you probably realized that is a crazy question – how can a window even have an offset?

In the past, jQuery has sometimes tried to make cases like this return something rather than having them throw errors. In this particular case of asking for the offset of a window, the answer up to now has been { top: 0, left: 0 } With this beta of jQuery 3.0 we’re experimenting with the idea of having such cases throw errors so that crazy requests aren’t silently ignored. Please try the beta and see if there is any code out there depending on jQuery to mask problems with invalid inputs.

.width(), .height(), .css(“width”), and .css(“height”) to return decimal values (whenever the browser does)

Previously, jQuery rounded values when retrieving width and height. Some browsers return subpixel values – such as IE and Firefox – and sometimes users need this precision when relying on these values for layout. We don’t expect this change to have a big impact on your code, but let us know if it does.

Removed deprecated event aliases

.load, .unload, and .error, deprecated since jQuery 1.8, are no more. Use .on() to register listeners.

Animations now use requestAnimationFrame

On platforms that support the requestAnimationFrame API, which is pretty much everywhere but IE9 and Android<4.4, jQuery will now use that API when performing animations. This should result in animations that are smoother and use less CPU time – and save battery as well on mobile devices.

jQuery tried using requestAnimationFrame a few years back but there were serious compatibility issues with existing code so we had to back it out. We think we’ve beaten most of those issues by suspending animations while a browser tab is out of view. Still, any code that depends on animations to always run in nearly real-time is making an unrealistic assumption.

.unwrap( selector )

Before jQuery 3.0, the .unwrap() method did not take any arguments. The selector parameter offers a way to be specific about which wrappers to remove.

jQuery.fn.domManip no longer accessible

jQuery.dir, jQuery.sibling, jQuery.buildFragment, jQuery.access, and jQuery.swap were all privatized in jQuery 1.12 and 2.2. These methods, along with jQuery.fn.domManip, were always intended for internal use only and were never documented. We are finally making them private to avoid confusion.

Massive speedups for some jQuery custom selectors

Thanks to some detective work by Paul Irish at Google, we identified some cases where we could skip a bunch of extra work when custom selectors like :visible are used many times in the same document. That particular case is up to 17 times faster now!

Keep in mind that even with this improvement, selectors like :visible and :hidden can be expensive because they depend on the browser to determine whether elements are actually displaying on the page. That may require, in the worst case, a complete recalculation of CSS styles and page layout! While we don’t discourage their use in most cases, we recommend testing your pages to determine if these selectors are causing performance issues.

This change actually made it into 1.12/2.2, but we wanted to reiterate it for jQuery 3.0.