# jQuery Migrate Plugin - Warning Messages
**NOTE: This page lists the messages for jQuery Migrate 4.x. If you are using an earlier version, see the documentation on [the 1.x-stable branch](https://github.com/jquery/jquery-migrate/blob/1.x-stable/warnings.md) or [the 3.x-stable branch](https://github.com/jquery/jquery-migrate/blob/3.x-stable/warnings.md).**
To allow developers to identify and fix compatibility issues when migrating older jQuery code, the development (uncompressed) version of the plugin generates console warning messages whenever any of its functionality is called. The messages only appear once on the console for each unique message.
**In most cases these messages are simply _warnings_; code should continue to work properly with later versions of jQuery as long as the jQuery Migrate plugin is used, but we recommend changing the code where possible to eliminate warnings so that the plugin does not need to be used.**
The production (compressed) version of the plugin does not generate warnings. To continue using jQuery code that has compatibility issues without making any changes and without console messages, simply include the production version in the file rather than the development version. See the [README](README.md) for download instructions.
All messages generated by this plugin start with the text "JQMIGRATE" for easy identification. The warning messages, causes, and remediation instructions are listed below. Items listed as **removed** - printed by `console.warn` must be changed before the code will work properly without the Migrate plugin. Items listed as only **deprecated** - printed by `console.info` - are still supported by the current version but no longer considered a good practice, and may be removed in the future.
## General logs & warnings
### JQMIGRATE: Migrate is installed, version X
### JQMIGRATE: Migrate is installed with logging active, version X
This is _not_ a warning, but a console log message the plugin shows when it first loads to indicate whether warnings will be shown on the console when appropriate. As of version 1.4.0 this message is also shown with production builds. The use jQuery Migrate in production has performance impacts and can complicate debugging as it modifies the normal behavior of the version of jQuery being used.
### JQMIGRATE: jQuery 4.x REQUIRED
**Cause:** The page does not have a version of jQuery installed, or is using a version of jQuery 5.0.0 or newer or older than 4.0.0. The jQuery Migrate plugin is not intended to be used for those cases. Any messages that follow this one may not be accurate, or the page may not run properly at all.
**Solution:** See the [README](https://github.com/jquery/jquery-migrate/#readme) for more information on usage and upgrading from older versions.
### JQMIGRATE: Migrate plugin loaded multiple times
**Cause:** The plugin detected that some version of jQuery Migrate is already loaded. Loading multiple versions can cause unpredictable behavior.
**Solution:** Remove all but the latest version of the jQuery Migrate plugin. See the [README](https://github.com/jquery/jquery-migrate/#readme) for more information on usage and upgrading from older versions.
### \[quirks\] JQMIGRATE: jQuery is not compatible with Quirks Mode
**Cause:** A browser runs in "quirks mode" when the HTML document does not have a `` as its first non-blank line, or when the doctype in the file is invalid. This mode causes the browser to emulate 1990s-era (HTML3) behavior. In Internet Explorer, it also causes many high-performance APIs to be hidden in order to better emulate ancient browsers. jQuery has never been compatible with, or tested in, quirks mode.
**Solution:** Put a [valid doctype](http://www.w3.org/QA/2002/04/valid-dtd-list.html) in the document and ensure that the document is rendering in standards mode. The simplest valid doctype is the HTML5 one, which we highly recommend: `` . The jQuery Migrate plugin does not attempt to fix issues related to quirks mode.
### \[load-after-event\] JQMIGRATE: jQuery(window).on('load'...) called after load event occurred
**Cause:** The calling code has attempted to attach a `load` event to `window` after the page has already loaded. That means the handler will never run and so is probably not what the caller intended. This can occur when the event attachment is made too late, for example, in a jQuery ready handler. It can also occur when a file is loaded dynamically with jQuery after the page has loaded, for example using the `$.getScript()` method.
**Solution:** If a function `fn` does not actually depend on all page assets being fully loaded, switch to a ready handler `$( fn )` which runs earlier and will always run `fn` even if the script that contains the code loads long after the page has fully loaded. If `fn` actually does depend on the script being fully loaded, check `document.readyState`. If the value is `"complete"` run the function immediately, otherwise use `$(window).on( "load", fn )`.
## Removed APIs
### \[fx-interval\] JQMIGRATE: jQuery.fx.interval is removed
**Cause**: As of jQuery 4.0, the `jQuery.fx.interval` property is no longer consulted and it's no longer possible to change the animation interval on browsers that do not support the `window.requestAnimationFrame()` method.
**Solution**: Find and remove code that changes or uses `jQuery.fx.interval`. If the value is being used by code in your page or a plugin, the code may be making assumptions that are no longer valid. The default value of `jQuery.fx.interval` is `13` (milliseconds), which could be used instead of accessing this property.
## \[boolean-attributes\] JQMIGRATE: Boolean attribute 'NAME' value is different from its lowercased name
### \[boolean-attributes\] JQMIGRATE: Boolean attribute 'NAME' value is not set to its lowercased name
**Cause**: Prior to jQuery 4.0, when calling `.attr( name, value )` with any non-`false` non-`null` `value`, jQuery would actually set it to `name`. Similarly, regardless of the actual value, `.attr( name )` used to return `name` lowercased. jQuery 4.0 removes this special behavior.
**Solution**: Always set boolean attributes to their names, whether when using jQuery (`.attr( name, name )`), native APIs (`.setAttribute( name, name )`) or directly in HTML (``). Avoid using the `.attr( name )` getter on boolean attributes, `.prop( name )` is preferred.
### \[attr-false\] JQMIGRATE: Setting the non-ARIA non-boolean attribute 'NAME' to false
**Cause**: Prior to jQuery 4.0, calling `.attr( name, false )` was only removing the attribute when `name` was a boolean attribute; otherwise, it was setting the attribute value to `"false"`. In jQuery 4.x, it will remove any non-ARIA attribute.
**Solution**: If you want to set the value of an attribute to `"false"`, wrap it in quotes: `.attr( name, "false" )`.
### \[parseJSON\] jQuery.parseJSON is removed; use JSON.parse
**Cause**: The `jQuery.parseJSON` method in recent jQuery is identical to the native `JSON.parse`. As of jQuery 4.0 `jQuery.parseJSON` is removed.
**Solution**: Replace any use of `jQuery.parseJSON` with `JSON.parse`.
### \[isNumeric\] JQMIGRATE: jQuery.isNumeric() is removed
**Cause**: This method was used by jQuery to determine if certain string arguments could be converted to numbers, but the name led people to apply their own interpretations to what the method means. As a result, it often doesn't meet the needs of specific cases. For example, a 25-character string of only digits is technically a valid number, but JavaScript cannot represent it accurately. The string `"0x251D"` is a valid hexadecimal number but may not be acceptable numeric input to a web form.
**Solution**: Use a test for being numeric that makes sense for the specific situation. For example, instead of `jQuery.isNumeric(string)`, use `isNan(parseFloat(string))` if a floating point number is expected, or `string.test(/^[0-9]{1,8}$/)` if a sequence of 1 to 8 digits is expected.
### \[type\] JQMIGRATE: jQuery.type() is removed
**Cause**: This method returns a string that indicates the type of the argument, for example `"number"` or `"function"`. However, as the JavaScript language evolves this method has become problematic because new language constructs might require this function to either return a new string (potentially breaking existing code) or somehow map new constructs into existing strings (again, potentially breaking existing code). Examples of new recent JavaScript features include asynchronous functions, class constructors, `Symbol`s, or functions that act as iterators.
**Solution**: Review code that uses `jQuery.type()` and use a type check that is appropriate for the situation. For example. if the code expects a plain function, check for `typeof arg === "function"`.
### \[push\] JQMIGRATE: jQuery.fn.push() is removed; use .add or convert to an array
### \[sort\] JQMIGRATE: jQuery.fn.sort() is removed; convert to an array before sorting
### \[splice\] JQMIGRATE: jQuery.fn.splice() is removed; use .slice() or .not() with .eq()
**Cause**: jQuery used to add the Array `push`, `sort` & `splice` methods to the jQuery prototype. They behaved differently to other jQuery APIs - they modify the jQuery collections in place, they don't play nice with APIs like `.end()`, they were also never documented.
**Solution**: Replace `.push( node )` with `.add( node )`, `.splice( index )` with `.not( elem.eq( index ) )`. In more complex cases, call `.toArray()` first, manipulate the resulting array and convert back to the jQuery object by passing the resulting array to `$()`.
### \[unique\] JQMIGRATE: jQuery.unique() is removed; use jQuery.uniqueSort()
**Cause**: The fact that `jQuery.unique` sorted its results in DOM order was surprising to many who did not read the documentation carefully. The function was renamed in jQuery 3.0 to make it clear. As of jQuery 4.0, the old alias no longer exists.
**Solution**: Replace all uses of `jQuery.unique` with `jQuery.uniqueSort` which is the same function with a better name.
### \[toggleClass-bool\] JQMIGRATE: `jQuery.fn.toggleClass( \[ boolean \] ) is removed`
**Cause:** Calling `.toggleClass()` with no arguments, or with a single Boolean `true` or `false` argument, has been deprecated. Its behavior was poorly documented, but essentially the method saved away the current `class` value in a data item when the class was removed and restored the saved value when it was toggled back. If you do not believe you are specificially trying to use this form of the method, it is possible you are accidentally doing so via an inadvertent undefined value, as `.toggleClass( undefined )` toggles all classes.
**Solution:** If this functionality is still needed, save the current full `.attr( "class" )` value in a data item and restore it when required.
### \[isFunction\] JQMIGRATE: jQuery.isFunction() is removed
**Cause:** This method returns `true` if its argument is thought to be a function. It was created to work around bugs in `typeof` implementations in legacy browsers.
**Solution:** Replace any use of `jQuery.isFunction( x )` with `typeof x === "function"`.
### \[isWindow\] jQuery.isWindow() is removed
**Cause:** This method returns `true` if its argument is thought to be a `window` element. It was created for internal use and is not a reliable way of detecting `window` for public needs.
**Solution:** Remove any use of `jQuery.isWindow()` from code. If it is truly needed it can be replaced with a check for `obj != null && obj === obj.window` which was the test used inside this method.
### \[nodeName\] JQMIGRATE: jQuery.nodeName() is removed
**Cause:** This public but never-documented method has been deprecated as of jQuery 3.2.0 and removed as of jQuery 4.0.0.
**Solution:** Replace calls such as `jQuery.nodeName( elem, "div" )` with a test such as `elem.nodeName.toLowerCase() === "div"`.
### \[cssProps\] JQMIGRATE: jQuery.cssProps is removed
**Cause:** The `jQuery.cssProps` property is a public but undocumented object that allows CSS properties with one name to be mapped into another name. It was used for legacy browsers like IE8 that used non-standard names. This object is no longer used inside jQuery since all supported browsers now use the standard CSS property names.
**Solution:** Remove any uses of `jQuery.cssProps` in application code.
### \[isArray\] JQMIGRATE: jQuery.isArray() is removed; use Array.isArray()
**Cause:** Older versions of JavaScript made it difficult to determine if a particular object was a true Array, so jQuery provided a cross-browser function to do the work. The browsers supported by jQuery 3.0 all provide a standard method for this purpose.
**Solution:** Replace any calls to `jQuery.isArray` with `Array.isArray`.
### \[trim\] JQMIGRATE: jQuery.trim() is removed; use String.prototype.trim
**Cause:** Older versions of IE & Android Browser didn't implement a method to `trim` strings so jQuery provided a cross-browser implementation. The browsers supported by jQuery 4.x all provide a standard method for this purpose.
**Solution:** Replace any calls to `jQuery.trim( text )` with `text.trim()` if you know `text` is a string; otherwise, you can replace it with `String.prototype.trim.call( text == null ? "" : text )`.
### \[css-number\] JQMIGRATE: Auto-appending 'px' to number-typed values for jQuery.fn.css( _(property name)_, value ) is removed
**Cause:** In past versions, when a number-typed value was passed to `.css()` jQuery converted it to a string and added `"px"` to the end. As the CSS standard has evolved, an increasingly large set of CSS properties now accept values that are unitless numbers, where this behavior is incorrect. It has become impractical to manage these exceptions in the `jQuery.cssNumber` object. In addition, some CSS properties like `line-height` can accept both a bare number `2` or a pixel value `2px`. jQuery cannot know the correct way to interpret `$.css("line-height", 2)` and currently treats it as `"2px"`.
**Solution:** Always pass string values to `.css()`, and explicitly add units where required. For example, use `$.css("line-height", "2")` to specify 200% of the current line height or `$.css("line-height", "2px")` to specify pixels. When the numeric value is in a variable, ensure the value is converted to string, e.g. `$.css("line-height", String(height))` and `$.css("line-height", height+"px")`.
### \[data-null-proto\] Accessing properties from jQuery.data() inherited from Object.prototype is removed
### \[data-null-proto\] Setting properties from jQuery.data() inherited from Object.prototype is removed
### \[data-null-proto\] Accessing properties from jQuery.fn.data() inherited from Object.prototype is removed
### \[data-null-proto\] Setting properties from jQuery.fn.data() inherited from Object.prototype is removed
**Cause:** As of jQuery 4.0.0, data objects no longer inherit from `Object.prototype`. This includes properties like `__proto__` or `hasOwnProperty`.
**Solution:** Don't use properties inherited from `Object.prototype` on data objects. Instead of `jQuery.data( node ).hasOwnProperty( "foo" )` use `Object.hasOwn( jQuery.data( node ), "foo" )` or, if you need to support older browsers like IE 11, use `Object.prototype.hasOwnProperty.call( jQuery.data( node ), "foo" )`.
### \[self-closed-tags\] JQMIGRATE: HTML tags must be properly nested and closed: _(HTML string)_
**Cause:** jQuery 3.5.0 changed the way it processes HTML strings. Previously, jQuery would attempt to fix self-closed tags like `` that the HTML5 specification says are not self-closed, turning it into ``. This processing can create a [security problem](https://nvd.nist.gov/vuln/detail/CVE-2020-11022) with malicious strings, so the functionality had to be removed.
**Solution:** Search for the reported HTML strings and edit the tags to close them explicitly. In some cases the strings passed to jQuery may be created inside the program and thus not searchable. Migrate warning messages include a stack trace that can be used to find the location of the usage in the code.
### \[jsonp-promotion\] JQMIGRATE: JSON-to-JSONP auto-promotion is removed
**Cause:** `jQuery.ajax` calls with `dataType: 'json'` with a provided callback are automatically converted by jQuery to JSONP requests unless the options also specify `jsonp: false`. Auto-promoting JSON requests to JSONP introduces a security risk as the developer may be unaware they're not just downloading data but executing code from a remote domain. This auto-promoting behavior has been removed in jQuery 4.0.0.
**Solution:** To trigger a JSONP request, specify the `dataType: "jsonp"` option.
### \[deferred-getStackHook\] JQMIGRATE: jQuery.Deferred.getStackHook is removed; use jQuery.Deferred.getErrorHook
**Cause:** `jQuery.Deferred.getStackHook` was originally created to pass the stack trace from before an async barrier to report when a user error (like calling a non-existing function) causes a promise to be rejected. However, passing a stack trace doesn't take source maps into account, so we started advising to pass the whole error object. To make it clearer, we also renamed the API to `jQuery.Deferred.getErrorHook`. The legacy alias has been removed in jQuery 4.0.0.
**Solution:** Rename all usage of `jQuery.Deferred.getStackHook` to `jQuery.Deferred.getErrorHook`. If you previously assigned a function returning an error stack to `jQuery.Deferred.getStackHook` or `jQuery.Deferred.getErrorHook`, change it to return a full error object. If you aim to still support jQuery <3.7, assign the hook to `jQuery.Deferred.getErrorHook` first and only later to `jQuery.Deferred.getStackHook` to avoid a Migrate warning.
### \[event-global\] JQMIGRATE: jQuery.event.global is removed
**Cause:** `jQuery.event.global` was an object with keys being event names for which event listeners have ever been added. Originally, it was needed for performance reasons and to fix memory leaks in old IE, but since jQuery 1.9.0, the library has only been recording the events, but it was not using that information anywhere. jQuery 4.0.0 removes the API.
**Solution:** Remove all usage of `jQuery.event.global`; it's unlikely any existing usage is needed.
## Deprecated APIs
### \[deferred-pipe\] JQMIGRATE: deferred.pipe() is deprecated
**Cause**: The `.pipe()` method on a `jQuery.Deferred` object was deprecated as of jQuery 1.8, when the `.then()` method was changed to perform the same function.
**Solution**: In most cases it is sufficient to change all occurrences of `.pipe()` to `.then()`. Ensure that you aren't relying on context/state propagation (e.g., using `this`) or synchronous callback invocation, which were dropped from `.then()` for Promises/A+ interoperability as of jQuery 3.0.
### \[pre-on-methods\] JQMIGRATE: jQuery.fn.bind() is deprecated
### \[pre-on-methods\] JQMIGRATE: jQuery.fn.unbind() is deprecated
### \[pre-on-methods\] JQMIGRATE: jQuery.fn.delegate() is deprecated
### \[pre-on-methods\] JQMIGRATE: jQuery.fn.undelegate() is deprecated
**Cause:**: These event binding methods have been deprecated in favor of the `.on()` and `.off()` methods which can handle both delegated and direct event binding. Although the older methods are still present in jQuery 4.x, they may be removed as early as the next major-version update.
**Solution**: Change the method call to use `.on()` or `.off()`, the documentation for the old methods include specific instructions. In general, the `.bind()` and `.unbind()` methods can be renamed directly to `.on()` and `.off()` respectively since the argument orders are identical.
### \[expr-pre-pseudos\] JQMIGRATE: jQuery.expr\[':'\] is deprecated; use jQuery.expr.pseudos
### \[expr-pre-pseudos\] JQMIGRATE: jQuery.expr.filters is deprecated; use jQuery.expr.pseudos
**Cause:** The standard way to add new custom selectors through jQuery is `jQuery.expr.pseudos`. These two other aliases are deprecated, although they still work as of jQuery 4.0.
**Solution:** Rename any of the older usage to `jQuery.expr.pseudos`. The functionality is identical.
### \[holdReady\] JQMIGRATE: jQuery.holdReady() is deprecated
**Cause:** The `jQuery.holdReady()` method has been deprecated due to its detrimental effect on the global performance of the page. This method can prevent all the code on the page from initializing for extended lengths of time.
**Solution:** Rewrite the page so that it does not require all jQuery ready handlers to be delayed. This might be accomplished, for example, by late-loading only the code that requires the delay when it is safe to run. Due to the complexity of this method, jQuery Migrate does not attempt to fill the functionality. If the underlying version of jQuery used with jQuery Migrate no longer contains `jQuery.holdReady()` the code will fail shortly after this warning appears.
### \[proxy\] JQMIGRATE: jQuery.proxy() is deprecated
**Cause:** This method, while having some differences, is similar to native `Function.prototype.bind` so it got deprecated to promote usage of native `bind`.
**Solution:** Replace any calls to `jQuery.proxy(fn, context, param1, param2)` with `fn.bind(context, param1, param2)`. Be careful if you use a proxied function for event handling as jQuery matches a proxied function to its original when removing event handlers which is not the case when native `bind` is used.
### \[shorthand-deprecated-v3\] JQMIGRATE: jQuery.fn.click() event shorthand is deprecated
**Cause:** The `.on()` and `.trigger()` methods can set an event handler or generate an event for any event type, and should be used instead of the shortcut methods. This message also applies to the other event shorthands, including: blur, focus, focusin, focusout, resize, scroll, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, mouseenter, mouseleave, change, select, submit, keydown, keypress, keyup, and contextmenu.
**Solution:** Instead of `.click(fn)` use `.on("click", fn)`. Instead of `.click()` use `.trigger("click")`.
### \[hover\] JQMIGRATE: jQuery.fn.hover() is deprecated
**Cause:** The `.hover()` method is a shorthand for the use of the `mouseover`/`mouseout` events. It is often a poor user interface choice because it does not allow for any small amounts of delay between when the mouse enters or exits an area and when the event fires. This can make it quite difficult to use with UI widgets such as drop-down menus. For more information on the problems of hovering, see the [hoverIntent plugin](http://cherne.net/brian/resources/jquery.hoverIntent.html).
**Solution:** Review uses of `.hover()` to determine if they are appropriate, and consider use of plugins such as `hoverIntent` as an alternative. The direct replacement for `.hover(fn1, fn2)`, is `.on("mouseenter", fn1).on("mouseleave", fn2)`.