插件窝 干货文章 升级到jQuery 3.6.1遇见的一些坑以及应对办法

升级到jQuery 3.6.1遇见的一些坑以及应对办法

jquery class arguments lt 35    来源:    2024-10-11

前言

项目因安全问题(jquery 早期版本存在XSS 漏洞),需要整体将 jQuery 从 1.7.2 升级到 3.6.1 。早前就因为 jQuery 的版本兼容性不佳,因此对于版本升级的事情,能拖则拖。但升级的事情,总归是要做的,何况项目上因为甲方重视安全性,对于中等风险的安全也必须解决、规避,因此这回是硬着头皮上了。

第一阶段的问题,就是各种不兼容,各种不顺。好在同事方法多,路子广,找到了一个 jquery-migrate-3.4.0.js 文件,专治新旧版本兼容的 包,解决了大多数的问题。UI 界面总算能跑起来,能看得着了。

但是接下来,陆续遇到了几个问题:

问题一:curCSS 方法找不到问题

解决方法:

    jQuery.curCSS = function(element, prop, val) {
        return jQuery(element).css(prop, val);
    };

问题二:UI 布局错误,多处动态计算 div 占用的宽度、高度 进行自适应布局的页面都拉跨了。

仔细分析后发现,罪魁祸首出现在  outerWidth 和 outerHeight 方法上。

这两个方法,老版本 jquery (以 1.7.2 为例),例如调用 $("body").outerWidth() ,是可以返回整数值的。但是新版的方法,如果不带参数,则返回 元素的 jQuery 对象。 而由于程序将之视为 int 参与计算,因此导致后续计算错误,界面布局拉跨也就难免了。

另外,老版本 jQuery  对于查找器搜索不到的情况,比如 $("#不存在的id").outerWidth() 是会返回 0 的,因为将一个不存在的元素的宽度视为0,也算是容错的,但是新版本在这种情况下,直接返回 undefined,这也会造成一些代码上的问题。

OK,知道原因,就可以有解决方案,如下:

   var oldOuterWidth = jQuery.fn.outerWidth;
    
    jQuery.fn.outerWidth =function(  ) {
        
        if (arguments.length <= 0) {
            arguments[0] = false;
            arguments.length = 1;
        }
        
        var result = oldOuterWidth.apply( this, arguments ) ;
        return result === undefined ? 0 : result;
    };
 
    var oldOuterHeight = jQuery.fn.outerHeight;
    jQuery.fn.outerHeight =function(  ) {
        
        if (arguments.length <= 0) {
            arguments[0] = false;
            arguments.length = 1;
        }
        
        var result =  oldOuterHeight.apply( this, arguments );
        return result === undefined ? 0 : result;
    };  

问题三、添加 html 内容 出错,页面元素 层次错乱。

比如,有以下代码:

$("body").append('<div><i class="c1" /><span>ok</span></div>');

在旧版 jQuery 当中,页面内容渲染得到:

<div><i class="c1"></i><span>ok</span></div>

但是在新版 jQuery 3.6.1 里得到的却是:

<div><i class="c1"><span>hello</span></i></div>

两者差别就是,针对 <i /> 的写法,渲染有不同。

造成这个差异的原因是什么呢?我们抛开 jQuery ,试试原生 JS 的效果:

document.children[0].children[1].innerHTML = '<div><i class="c1" /><span>ok</span>';

结果渲染得到的与 jQuery 3.6.1 是一致的。也就是针对 <i /> 这种写法,HTML 5 是不认的,因为 <i> 本身不属于 HTML5 非闭合标签 (所谓 非闭合标签,是类似 <meta> <br> 这种,数量有限),则 渲染引擎 将 <i /> 视为未闭合。实际上 <element /> 的写法,属于 XHTML ,而不是 HTML5 的规范。

奇怪的是,为啥旧版 jQuery 我们用着就没有问题,难道它在 append 方法里做了什么特殊处理? 

排查了源码,果不其然。如下:

// Convert html string into DOM nodes
            if ( typeof elem === "string" ) {
                if ( !rhtml.test( elem ) ) {
                    elem = context.createTextNode( elem );
                } else {
                    // Fix "XHTML"-style tags in all browsers
                    elem = elem.replace(rxhtmlTag, "<$1></$2>");

注释上明确说了 Fix "XHTML"-style tags in all browsers  。哎,我们这些一直误用的人啊,被老版本 jQuery 保护的太好了!

OK,为了确保此前的UI代码能继续使用,我们还是要得来个解决方案:

   var oldAppend = jQuery.fn.append;
    
    jQuery.fn.append =function(  ) {
        
        /**
         * 相关素材来自 jquery 1.7.2 
         */
        var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig;
        
        if (arguments.length > 0 
            && typeof(arguments[0]) == "string" ) {
            var elem = arguments[0];
            elem = elem.replace(rxhtmlTag, "<$1></$2>");
            arguments[0] = elem;
        }
        
        return oldAppend.apply( this, arguments );
    };

至此,遇到的不兼容问题基本都解决了。我们将包含前述问题的解决办法,汇总到 以下 js 代码里,需要的各位可以自取。

 
( function() { 
    
    /**
     * 注意,以下代码,应确保 引用在 jquery 3.6.1, jquery-migrate-3.4.0.min.js,以及 jquery-ui-1.8.10.custom.min.js 等内容之后,否则
     * 相关的 代码托管 可能会被取代 而 无效。   
     */
    
    jQuery.curCSS = function(element, prop, val) {
        return jQuery(element).css(prop, val);
    };
    
    /**
     *  James add on 2022.11.24 , 相关代码摘录自 jquery 1.7.2
     *  此 修复是为了 兼容 以下有关写法,在 append 到 jquery 对象里时,能适应 老版本的渲染逻辑。
     *     $.append("<div><i class='abc' /> <span>ttt</span></div>");
     *   上述内容,在 jquery 1.7.2 版本里,会被渲染为:
     *     <div><i class='abc'></i><span>ttt</span></div>
     *    
     *    而在 jquery 3.6.1 里,则会被渲染为:
     *    <div><i class='abc'><span>ttt</span></i></div>
     *    
     *    这是由于 在 旧版 jquery 里,针对 非闭合的标签有过特定处理,而在 jquery 3.6.1  里则移除了这个做法。
     *    因此直接在 element 的 innterHtml 里面写入 <div><i class='abc' /> <span>ttt</span></div> 这个内容,
     *    与 3.6.1 里面是一致的。
     *    
     *    所以根本上是不规范的 html 写法,只是老版本 jquery 做了容错,而新版本没有容错,因而导致问题。
     *     
     */
    var oldAppend = jQuery.fn.append;
    
    jQuery.fn.append =function(  ) {
        
        /**
         * 相关素材来自 jquery 1.7.2 
         */
        var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig;
        
        if (arguments.length > 0 
            && typeof(arguments[0]) == "string" ) {
            var elem = arguments[0];
            elem = elem.replace(rxhtmlTag, "<$1></$2>");
            arguments[0] = elem;
        }
        
        return oldAppend.apply( this, arguments );
    };
    
    
    var oldOuterWidth = jQuery.fn.outerWidth;
    
    jQuery.fn.outerWidth =function(  ) {
        
        if (arguments.length <= 0) {
            arguments[0] = false;
            arguments.length = 1;
        }
        
        var result = oldOuterWidth.apply( this, arguments ) ;
        return result === undefined ? 0 : result;
    };
 
    var oldOuterHeight = jQuery.fn.outerHeight;
    jQuery.fn.outerHeight =function(  ) {
        
        if (arguments.length <= 0) {
            arguments[0] = false;
            arguments.length = 1;
        }
        
        var result =  oldOuterHeight.apply( this, arguments );
        return result === undefined ? 0 : result;
    };  
    
})();

注意,上述代码在页面中的引入,应确保在 jquery 3.6.1, jquery-migrate-3.4.0.min.js,以及 jquery-ui-1.8.10.custom.min.js 等内容之后,否则可能会被取代而无效。 

总结

到此这篇关于升级到jQuery 3.6.1遇见的一些坑以及应对办法的文章就介绍到这了,更多相关升级jQuery 3.6.1的坑内容请搜索插件窝以前的文章或继续浏览下面的相关文章希望大家以后多多支持插件窝!