jQuery Core 3.5 Upgrade Guide


link jQuery.htmlPrefilter changes

link A workaround

If you want to upgrade to jQuery 3.5.0 or newer and don't have time to deal with breaking changes at the moment and you use jQuery Migrate 3.2.0 or newer, you can revert to the previous behavior by invoking:

1
jQuery.UNSAFE_restoreLegacyHtmlPrefilter();

jQuery Migrate 3.3.0 or newer logs warnings for all input that's affected by this change, regardless of whether the jQuery.UNSAFE_restoreLegacyHtmlPrefilter() method was called or not. Note: if you overwrite jQuery.htmlPrefilter manually, you'll lose those warnings!

If you don't use jQuery Migrate, don't add it just for this one workaround. Instead, you can revert to the previous behavior by redefining jQuery.htmlPrefilter after loading jQuery:

1
2
3
4
var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi;
jQuery.htmlPrefilter = function( html ) {
return html.replace( rxhtmlTag, "<$1></$2>" );
};

Note that if you do this, you lose the jQuery 3.5.0 security fix and you have to be more careful with what HTML you pass to jQuery manipulation methods; regular HTML sanitizing will not be enough. Some security libraries have special sanitization settings for jQuery. For example, DOMPurify has a SAFE_FOR_JQUERY flag:

1
2
var sanitizedHtml = DOMPurify.sanitize( unsafeHtml, { SAFE_FOR_JQUERY: true } );
elem.html( sanitizedHtml );

link Description of the change

jQuery.htmlPrefilter modifies and filters HTML strings passed through jQuery manipulation methods. The default value of this function in jQueries older than 3.5.0 used to replace XHTML-like tags with versions that work in HTML. For example, previously the following:

1
jQuery( "<div/><span/>" );

would create the following structure:

1
2
<div></div>
<span></span>

because <div/> was replaced with <div></div> & <span/> with <span></span>.

In jQuery 3.5.0, the jQuery.htmlPrefilter method always returns its argument unchanged. Modern browsers usually parse and render HTML strings in HTML mode. Elements such as <p> and <span> are never self-closing in HTML, so the browser interprets a string like <p/><span/> as <p><span> and leaves the tags open. The browser closes the tags at the end of the string or at an element that cannot be contained in the element, so another <p> tag closes an existing open <p> tag.

1
2
3
<div>
<span></span>
</div>

To avoid this, don't use self-closing tags for tags that may have content unless your page runs in XHTML mode. Make sure you're sending a correct mime type: application/xhtml+xml; otherwise, your page will really run in HTML mode.

If you're writing a library and you want it to work both in HTML & XHTML modes, remember to use self-closing tags for empty elements, i.e. ones that don't have closing tags in HTML. For example, instead of:

1
jQuery( "<div/><img/>" );

do this:

1
jQuery( "<div></div><img/>" );

One popular input that still works in jQuery 3.5.0 or newer is the one with a single tag with or without attributes:

1
jQuery( "<div class='yellow' />" );

This is becuse it's XHTML-compliant and in HTML the parser first changes it to just the opening tag: <div class='yellow'> but then immediately auto-closes it as it reaches the end of input.