jQuery 4.0 - Prepare to fix your Javascript code

Adapt and fix your existing Javascript code to work with both versions of jQuery 3.6 and the upcoming jQuery 4.0 which will bring back a major backward incompatible change introduced by jQuery 3.5.0 and rolled back in jQuery 3.5.1

Make your javascript code compliant with the upcoming jQuery 4.0

This article will show you how to adapt your existing code to be prepared for the jQuery next, 4.0.

The jQuery 3.5.0 introduced a major and unexpected backward incompatible change that have been rolled back in jQuery 3.5.1 but will be keep back in jQuery 4.0

Two years ago jQuery 3.5.0 has been released (2020). Immediately after jQuery 3.5.1 have been made available to fix back a regression which made most of the developers' existing code to be backward incompatible or broken. This rollback change was also kept in the latest stable version of jQuery 3.6.1 which has been released in 2022. But this change will be reverted again (and will be keep exactly as introduced in jQuery 3.5.0) in the upcoming jQuery 4.0, as announced by jQuery team at that time jQuery changelog. This specific change which will make a lot of old jQuery based code and plugins unusable without adapting and fixing that code.

The time passed and the new jQuery 4.0 is almost here. Keeping jQuery 3.6 for a while just for the sake of old code and plugins ... yes, can work for a while, but at some point there will be an objective decision to go with the newer jQuery 4.0 in order to have the benefits and optimization support for the newer browsers instead supporting old browsers and old code !

The most notable change introduced by jQuery 3.5.0 (rolled back in 3.5.1 as in 3.6.0, 3.6.1 / will be back in jQuery 4.0): jQuery internal data object will be using Object.create(null) instead of the plain object {} as before ... which breaks a lot of old code and old plugins .

The reason behind this change is to prevent possible collisions with keys on Object.prototype properties. See below the announcement, with their motivation, as written by jQuery team, extracted from the jQuery 3.5.1 changelog about this specific change introduced in jQuery 3.5.0.
Specifically, we had changed our internal data object to use Object.create( null ) instead of a plain object ({}). We did that to prevent collisions with keys on Object.prototype properties. However, this also meant that developers (especially plugins) could no longer check what was in jQuery data with the native .hasOwnProperty() method, and it broke some code. We’ve reverted that change, but plan to put it back in jQuery 4.0. This change is the only code change in this release (3.5.0). Other changes include some minor updates to our docs and build system.

Solution: how to fix the old jQuery dependent code to work with all three versions jQuery 3.5.0 / 3.5.1 / 3.6.0 / 3.6.1 and be prepared for the upcoming jQuery 4.0 for the upcoming internal jQuery change that address the use of Object.create(null) instead of {}
This fix implies searching all over the code for the Object.hasOwnProperty() method of any object created by jQuery and change it to Object.prototype.hasOwnProperty.call(). Not so simple ... it depends on context, but is doable.
Example: fixing the old Bootstrap v3.4.1 code, for the issue described above, summary:
  • replace obj.hasOwnProperty() with Object.prototype.hasOwnProperty.call() for all contexts where the returned jQuery object is {} instead of Object instance ; will work in all existing jQuery versions: 3.5.0, 3.5.1, 3.6.0, 3.6.1 ; will also work with the expected jQuery 4.0.
This is a hard and very sensitive fix that have to be tested a lot after adapting existing code. By interpolating this example you can see how to deal with this code fix that have to be implemented in all the jQuery existing code and plugins where this context is found to make that code ready and fixed for the upcoming jQuery 4.0 but also keep compatibility with jQuery 3.6 and older versions.
At the time this article is being written the Bootstrap.js is at a newer version than the sample code below. The sample fix below address the Boostrap.js 3.4.1 just as an example for how to deal and fix this specific issue.
 * Bootstrap v3.4.1 (https://getbootstrap.com/)
 * Copyright 2011-2019 Twitter, Inc.
 * Licensed under the MIT license

// changes to this file: bootstrap.js
// use Object.prototype.hasOwnProperty.call() instead of obj.hasOwnProperty() when jQuery returns {} instead of Object
// (c) 2022 w3soft.org
// license BSD

// ...

Tooltip.prototype.getOptions = function (options) {
		var dataAttributes = this.$element.data()

		for (var dataAttr in dataAttributes) {
		//	if (dataAttributes.hasOwnProperty(dataAttr) && $.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1) { // original code, does not work in jQuery 3.5.0 and upcoming 4.0 ; does work in jQuery 3.5.1 ... 3.6.1
		// #start fix: the above line is fixed as below and will work in all jQuery target versions: jQuery 3.5.0 / 3.5.1 / 3.6.0 / 3.6.1 and the upcoming jQuery 4.0
			if (
				dataAttributes && dataAttr && Object.prototype.hasOwnProperty.call(dataAttributes, dataAttr) &&
				$.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1
			) {
		// #end fix (c) w3soft.org
				delete dataAttributes[dataAttr]

		options = $.extend({}, this.getDefaults(), dataAttributes, options)

		if (options.delay && typeof options.delay == 'number') {
			options.delay = {
				show: options.delay,
				hide: options.delay

		if (options.sanitize) {
			options.template = sanitizeHtml(options.template, options.whiteList, options.sanitizeFn)

		return options

// ...

The second notable change introduced by jQuery 3.5.0 that was kept in all later versions: jQuery 3.5.1 ... 3.6.1 and will be also in the upcoming jQuery 4.0 is regarding the usage of XHTML tags with jQuery objects

This change is a security fix for an issue regarding the jQuery.htmlPrefilter, but with the cost that a lot of old code and plugins have to be adapted.
jQuery used a regex in its jQuery.htmlPrefilter method to ensure that all closing tags were XHTML-compliant when passed to methods. For example, this prefilter ensured that a call like jQuery('<div class="code" />') is actually converted to jQuery('<div class="code"></div>'). Recently, an issue was reported that demonstrated the regex could introduce a cross-site scripting (XSS) vulnerability.
The HTML parser in jQuery <=3.4.1 usually did the right thing, but there were edge cases where parsing would have unintended consequences. The jQuery team agreed it was necessary to fix this in a minor release, even though some code relies on the previous behavior and may break. The jQuery.htmlPrefilter function does not use a regex in 3.5.0 and passes the string through unchanged.

Solution: how to fix the old jQuery code and plugins that are still using unsafe XHTML code (which no more works correctly since jQuery 3.5.0) with HTML5 compliant code
This is an easy fix. It can be made in minutes and will fix 100% all the old code adressing this issue. XHTML is no more supported officially and all the code must be HTML5 ready. There is a drawback, the minified javascript files will be a bit larger since many developers were using the XHTML tags as an alternative for a shorter syntax instead of the full HTML5 compliant syntax, but this is no more possible ...
// sample code
// (c) 2022 w3soft.org
// license BSD

// old jQuery code, works until jQuery 3.4.1 inclusive ; !!! does NOT work correctly with jQuery 3.5.0 ... 3.6.1 ; will NOT work correctly with jQuery 4.0 !!!
var $div = jQuery('<div class="code" />'); // example 1
var $ul = jQuery('<ul />'); // example 2
var $select = jQuery('<select><option /></select>'); // example 3

// new, fixed jQuery code, works until jQuery 3.4.1 inclusive ; does also work with jQuery 3.5.0 ... 3.6.1 ; will work also with jQuery 4.0
var $div = jQuery('<div class="code"></div>'); // example 1, revised and fixed
var $ul = jQuery('<ul></ul>'); // example 2, revised and fixed
var $select = jQuery('<select><option></option></select>'); // example 3, revised and fixed

Preparing a modified version of jQuery 3.6 just for testing purposed only, to be prepared for the upcoming jQuery 4.0 which contains the rolled back specific change (objects) introduced in the version 3.5.0 and rolled back in version 3.5.1 of jQuery.

Using the jQuery 3.5.0 instead of this modified version for testing purposes is a worst idea since there are many other fixes between 3.5.0 ... 3.6.1 that will be keep in the jQuery 4.0 and later versions, so this slighly modified version of jQuery 3.6 is the best available for testing ...
 * jQuery JavaScript Library v3.6.1 [ #modified, based on v3.6.1, with upstream fixes reverted from jQuery 3.5.0 for {} -> Object.create( null ) ]
 * https://jquery.com/
 * Includes Sizzle.js
 * https://sizzlejs.com/
 * Copyright OpenJS Foundation and other contributors
 * Released under the MIT license
 * https://jquery.org/license
 * Date: 2022-08-26T17:52Z

// ...

Data.prototype = {

	cache: function( owner ) {

		// Check if the owner object already has a cache
		var value = owner[ this.expando ];

		// If not, create one
		if ( !value ) {
			//-- # start fix: by w3soft.org
		//	value = {}; // this was the original code for this line before jQuery 3.5.0 and also in jQuery 3.5.1 ... 3.6.1
			value = Object.create( null ); // the above commented line was changed in jQuery 3.5.0, to prevent collisions ; rolled back as above in jQuery 3.5.1 ... 3.6.1 ; will be keep back in jQuery 4.0 and later
			//-- #end fix

// ...


jQuery had a very clear policy for keeping it backward compatible, but when a security issue is found or the new browsers are introducing new features or some old browsers are dissapearing then jQuery have to deal with some internal changes too. The changes presented in this article are not something subjective but being very objective in their reasons.