zoukankan      html  css  js  c++  java
  • 记jQuery.fn.show的一次踩坑和问题排查

    最近很少已经很少用jQuery,因为主攻移动端,常用Zepto,其实很多细节和jQuery并不一样。 最近又无意中接触到了PC的需求和IE6, 使用了jQuery,刚好踩坑了,特意记录一下。

    > 本文内容如下: > - 问题 > - 解决 > - jQuery.fn.show()和jQuery.fn.show(0)到底发生了什么 > - 结语 > - 参考和引用

    JavaScript - 前端开发交流群:377786580

    问题

    最近很少用jQuery,因为主攻移动端,常用Zepto,其实很多细节和jQuery并不一样。以前读过Zepto的源码,所以完全知道zepto.fn.show/zepto.fn.hide到底做了什么。
    一直只记得jQuery.fn.show/jQuery.fn.hide方法在后来被改写过,也没怎么去关注过这里,最近又无意中接触到了PC的需求和IE6, 使用了jQuery,刚好踩坑了,特意记录一下。
    问题就是有一个<a>标签默认是隐藏的(display:none),做了一些业务逻辑的处理之后呢把这个<a>显示出来(通过jQuery.fn.show),但是这个<a>标签却被加上了新的样式:

        <a href="javascript:;" style="display:inline-block;"></a>
    

    查了一下当时调用jQuery.fn.show的代码:

        $('#id').show(0); //给a加上了display:inline-block;
    

    解决

    触发这个问题就是因为自己错误的以为jQuery.fn.show/jQuery.fn.hide会有默认动画时间,于是显示隐藏元素的时候喜欢这样处理:

    	$('#id').show(0);
    	$('#id').hide(0);
    

    其实是自己对jQuery.fn.show/jQuery.fn.hide记错了,以前学jQuery的时候就只记得了个jQuery动画有个默认的时间,是300ms。但没记API,其实是:

    jQuery的动画系列函数,slideDown、slideUp、slideToggle、fadeIn、fadeOut、fadeToggle、fadeTo、toggle都是默认400ms动画时间的,而show/hide默认是没有动画时间的。

    参见jQuery 1.11.0源码:

    	jQuery.each({
    		slideDown: genFx("show"),
    		slideUp: genFx("hide"),
    		slideToggle: genFx("toggle"),
    		fadeIn: { opacity: "show" },
    		fadeOut: { opacity: "hide" },
    		fadeToggle: { opacity: "toggle" }
    	}, function( name, props ) {
    		jQuery.fn[ name ] = function( speed, easing, callback ) {
    			return this.animate( props, speed, easing, callback );
    		};
    	});
    

    合并动画配置(设置动画默认时间)的代码是jQuery.speed方法:

    	jQuery.speed = function( speed, easing, fn ) {
    		var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
    			complete: fn || !fn && easing ||
    				jQuery.isFunction( speed ) && speed,
    			duration: speed,
    			easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
    		};
    		
    		opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
    			opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default /*默认动画时间(ms)*/;
    		if ( opt.queue == null || opt.queue === true ) {
    			opt.queue = "fx";
    		}
    		opt.old = opt.complete;
    		opt.complete = function() {
    			if ( jQuery.isFunction( opt.old ) ) {
    				opt.old.call( this );
    			}
    	
    			if ( opt.queue ) {
    				jQuery.dequeue( this, opt.queue );
    			}
    		};
    		return opt;
    	};
    	
    	//...
    	
    	jQuery.fx.speeds = {
    		slow: 600,
    		fast: 200,
    		// Default speed
    		_default: 400
    	};
    

    后来查到问题是给方法传递了一个参数0导致的问题,把参数去掉即可,改成下面的代码即可:

        <script>
            $('#id').show();
        </script>
        <a href="javascript:;" style=""></a>
    

    然后被妹子追问为什么,回答不上来,只记得这俩API后来调整过。

    jQuery.fn.show()和jQuery.fn.show(0)到底发生了什么

    之后我就觉得有必要再去深入了解下jQuery.fn.show/jQuery.fn.hideAPI了,至少要了解下这俩API现在到底做了什么。

    刚开始我仍然天真的以为jQuery.fn.show在不传递参数的情况下会有默认动画时间,直到翻阅源码版本至jQuery 1.4.0发现都对没有参数的情况进行了display的处理,反而是传递了speed之后会走动画处理。

    jQuery.fn.extend({
    	//$('id').show();
    	show: function( speed, callback ) {
    		if ( speed != null ) {
    			return this.animate( genFx("show", 3), speed, callback);
    		} else {
    			//speed===undefined
    			for ( var i = 0, l = this.length; i < l; i++ ) {
    				//操作元素的display
    				var old = jQuery.data(this[i], "olddisplay");
    				this[i].style.display = old || "";
    				if ( jQuery.css(this[i], "display") === "none" ) {
    					var nodeName = this[i].nodeName, display;
    					if ( elemdisplay[ nodeName ] ) {
    						display = elemdisplay[ nodeName ];
    					} else {
    						var elem = jQuery("<" + nodeName + " />").appendTo("body");
    						display = elem.css("display");
    						if ( display === "none" ) {
    							display = "block";
    						}
    						elem.remove();
    						elemdisplay[ nodeName ] = display;
    					}
    					jQuery.data(this[i], "olddisplay", display);
    				}
    			}
    			for ( var j = 0, k = this.length; j < k; j++ ) {
    				this[j].style.display = jQuery.data(this[j], "olddisplay") || "";
    			}
    			return this;
    		}
    	},
    
    	hide: function( speed, callback ) {
    		if ( speed != null ) {
    			return this.animate( genFx("hide", 3), speed, callback);
    		} else {
    			for ( var i = 0, l = this.length; i < l; i++ ) {
    				var old = jQuery.data(this[i], "olddisplay");
    				if ( !old && old !== "none" ) {
    					jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
    				}
    			}
    			for ( var j = 0, k = this.length; j < k; j++ ) {
    				this[j].style.display = "none";
    			}
    			return this;
    		}
    	}
    });
    
    

    jQuery 1.11.0代码整理后阅读起来好了很多:

    	jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
    		var cssFn = jQuery.fn[ name ];
    		jQuery.fn[ name ] = function( speed, easing, callback ) { //$('#id').show();
    			//speed===undefined
    			return speed == null || typeof speed === "boolean" ?
    				cssFn.apply( this, arguments ) :
    				this.animate( genFx( name, true ), speed, easing, callback );
    		};
    	});
    

    可以看见当在jQuery 1.11.0中,当speed参数为null、undefined、boolean的时候会进入css直接处理分支,而不会走动画

    所以下面的代码完全走的是不同的分支:

    	$('#id').show(); //走css处理分支
    	$('id').show(0); //走animate处理分支
    

      

    那么<a>上的display:inline-block从何而来呢?于是搜索了一下inline-block,发现了这一段代码:

    function defaultPrefilter( elem, props, opts ) {
    	/* jshint validthis: true */
    	var prop, value, toggle, tween, hooks, oldfire, display, dDisplay,
    		anim = this,
    		orig = {},
    		style = elem.style,
    		hidden = elem.nodeType && isHidden( elem ),
    		dataShow = jQuery._data( elem, "fxshow" );
    	
    	// ...
    
    	// height/width overflow pass
    	if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
    		opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
    		display = jQuery.css( elem, "display" ); //获取元素样式中的display
    		dDisplay = defaultDisplay( elem.nodeName ); //获取元素默认的display
    		if ( display === "none" ) {
    			display = dDisplay;
    		}
    		if ( display === "inline" && jQuery.css( elem, "float" ) === "none" ) {
    			if ( !support.inlineBlockNeedsLayout || dDisplay === "inline" ) {
    				//现代浏览器下,当元素默认的display为inline,则调整为inline-block
    				style.display = "inline-block";
    			} else {
    				style.zoom = 1;
    			}
    		}
    	}
    
    	// ...
    }
    

    defaultPrefilter方法在jQuery内置的动画对象Animation中调用,用于修正执行动画前的一些细节。
    到了这里再想想动画的一些操作因素,也就可以理解为什么会这样了:

    jQuery中的动画操作的无非是位置、透明度、宽高几个点。
    有些动画涉及到宽高的改变(例如:jQuery.fn.show/jQuery.fn.hide改变的就是元素的width+heightjQuery.fn.slideDown改变的是height),那么就需要把这些元素设置成可以改变宽高的(行)块级元素。因为行级元素是不能通过css改变宽高的。
    而<a>标签默认是inline的,当对它操作width/height(即jQuery.fn.show)的时候,需要把它改成inline-block,让动画对它设置的宽高生效。

    结语

    至此,问题追查完毕,我也可以好好的跟妹子吹吹牛逼啦~~~~~~~当然除了这个,更多的是自己的反思,想起了之前做的代码里很多时候使用的都是:

    	$('#id').show(0);
    

    加这一个参数和不加参数光看看源码就知道是天壤之别了,更别提什么性能,IE678套餐可能造成的影响了。
    其实问题的根源仍然在于我对代码的了解程度不够,对于当前代码的认知自己竟然觉得满足了。想想刚学js的我可是充满了好奇心各种探索造轮子查资料读源码,随着时间的推移,技术实力和视野逐渐的提升,让自己变得越来越懒惰,越来越容易满足,这样的状态并不好。
    任何时候,都应该清楚的知道自己撸的代码到底发生了什么,学无止境,共勉。

    JavaScript - 前端开发交流群:377786580

    作者:linkFly
    声明:嘿!你都拷走上面那么一大段了,我觉得你应该也不介意顺便拷走这一小段,希望你能够在每一次的引用中都保留这一段声明,尊重作者的辛勤劳动成果,本文与博客园共享。
  • 相关阅读:
    hdu 2647 Reward
    hdu 2094 产生冠军
    hdu 3342 Legal or Not
    hdu 1285 确定比赛名次
    hdu 3006 The Number of set
    hdu 1429 胜利大逃亡(续)
    UVA 146 ID Codes
    UVA 131 The Psychic Poker Player
    洛谷 P2491消防 解题报告
    洛谷 P2587 [ZJOI2008]泡泡堂 解题报告
  • 原文地址:https://www.cnblogs.com/silin6/p/jQuery-fn-show.html
Copyright © 2011-2022 走看看