jQuery 3.0 Beta Released

Posted on by

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. As users increasingly embrace the Promise pattern for asynchronous work like AJAX, the idea of having special cases with duplicate method names for the Promise returned by jQuery.ajax is an increasingly bad idea. So, these deprecated names have been removed. Instead of the special-case success, error, complete use the corresponding done, fail, always methods.

Note that this does not have any impact at all on the success, error, complete callbacks of the same name, which continue to exist and are not deprecated. This only affects the Promise methods! It also does not remove or deprecate the abort method.


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.


28 thoughts on “jQuery 3.0 Beta Released

  1. When do you think you will drop support for IE9 and IE10?
    Did you talk about this? Are there any plans?

  2. Guillermo Ignacio Enriquez Gutierrez on said:

    Btw, microsoft didnt say completely bye bye to ie9 or ie10. They are both supported on Vista for a year or so. Maybe that is the reason jQuery is still supporting ie9, etc

  3. Timmy Willison on said:

    @Phill I don’t think you read that right. jQuery is now smaller than it was way back in 1.5.2. But really, the point of that page is to prove the importance of gzip, which makes increases and decreases in size largely insignificant.

  4. I’m sorry that you also don’t drop support for IE9 or IE10. Even if Microsoft didn’t do it, you should, because it’s up to groups like the JQuery Foundation that helps to shape the future of the Internet by giving the example.

  5. @César Couto, why should it be up to jQuery to drop support before anyone else? Many people, like myself, use jQuery as a way to make my javascript work on older browsers (for sites that need it), along with dealing with some cross-browser issues.
    But on the other hand, I guess I could just use older jQuery versions for those sites that require older browser compatibility.

  6. Jesús Perales on said:

    Excellent !!, the breaking changes its a little price to pay for more performance, die Internet Explorer !!!

  7. @Guillermo Ignacio Enriquez Gutierrez
    > microsoft didnt say completely bye bye to ie9 or ie10. They are both supported on Vista

    Not both, only IE 9 is (until April 2017). IE 10 is not supported on any consumer version of Windows anymore.

  8. Daniel Hagnoul on said:

    Cette version me déçoit profondément.

    Vu la sortie de jQuery 1.12.0 et 2.2.0, on pouvait attendre 6 mois de plus la branche 3.

    Le support d’IE 9-11 par la branche 3 est incompréhensible, les versions précédentes servent à cela.

    Lorsqu’on produit une version non rétrocompatible, on en profite pour “casser la baraque” et reconstruire les fondations.

    Il fallait attendre que les navigateurs d’aujourd’hui supportent à 90 % ES2015 (c’est déjà le cas pour les prochaines versions de Edge, de Chrome et peut-être Firefox que je n’utilise plus) pour supprimer de jQuery tout ce qui est natif dans ES2015 et produire une version épurée au maximum de toutes les obsolescences.

    À quoi peut servir jQuery.Deferred lorsque Promise/A+ est natif dans ES2015 ? De même pour les transactions AJAX et l’inénarrable inventaire des méthodes de l’objet jqXHR.

  9. Merlin Calo on said:

    Thanks for the update, and the work by those that have contributed to this great project over the years. I am curious to how the jQuery performs on a mobile browser right now. Are the references in the above comments towards performance referring to a browser on desktop/laptop computer or mobile browser? Where is a good place to test the performance for jQuery on your own?

  10. Another major point of Promises/A+ compatibility that should not be neglected is that any thenables will be assimilated when being returned from a `.then()` callback.
    This means that code which creates a promise for an object with a `then` method will break, but also that you can savely return promises from *any* implementation from a `then` callback (without needing to wrap them using the deferred antipattern).
    This also means you cannot have a Deferred that is fulfilled with another Deferred any more – which you hopefully never had anyway.

    Btw, will we see ES6-inspired things like $.Deferred.resolve or $.Deferred.all, that are standard in proper promise implementations?

  11. Another major point of Promises/A+ compatibility that should not be neglected is that any thenables will be assimilated when being returned from a `.then()` callback.

  12. Oriman on said:

    I too vote for dropping support for IE9/10 on jQuery3.
    Sites that require IE9/10 support can keep using jQuery 2.2 forever.

  13. Duncan Booth on said:

    One part of the description of the promise behaviour might be easily misunderstood:

    “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”.”

    The grandchildren will indeed be rejected, but in the example nothing is logged to show this as the “rejected” callback is only invoked if the child is rejected. I think the description could be easily misread as saying that the grandchildren would log “rejected baz”. (I misread it that way and as that reading wouldn’t be Promise A+ compliant I had to go and verify that the behaviour is indeed compliant).

    It might be useful to clarify this: exceptions in the fulfillment callback become rejections further down the chain, but they won’t invoke the rejection callback at the same level.