feature/gyt
gyt 3 weeks ago
parent 218b75bc32
commit 677cd46203

@ -8558,34 +8558,34 @@
for ( name in prop ) {
val = prop[ name ];
// easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
// 缓动的处理逻辑:根据每个属性 > opt.specialEasing > opt.easing > 'swing'(默认)
if ( jQuery.isArray( val ) ) {
// 如果 val 是数组,将 opt.animatedProperties 中 name 对应的属性设置为 val[1]
opt.animatedProperties[ name ] = val[ 1 ];
// 将 prop 中 name 对应的属性设置为 val[0]
val = prop[ name ] = val[ 0 ];
} else {
opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
// 否则,设置 opt.animatedProperties 中 name 对应的属性为 opt.specialEasing 中 name 对应的属性,如果不存在则使用 opt.easing再不存在使用'swing'
opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing ||'swing';
}
if ( val === "hide" && hidden || val === "show" && !hidden ) {
// 如果 val 是 'hide' 且 hidden 为真,或者 val 是'show' 且 hidden 为假,调用 opt.complete 函数
if ( val === "hide" && hidden || val === "show" &&!hidden ) {
return opt.complete.call( this );
}
if ( isElement && ( name === "height" || name === "width" ) ) {
// Make sure that nothing sneaks out
// Record all 3 overflow attributes because IE does not
// change the overflow attribute when overflowX and
// overflowY are set to the same value
// 确保没有遗漏
// 记录所有 3 个 overflow 属性,因为 IE 在 overflowX 和 overflowY 被设置为相同值时不会改变 overflow 属性
opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
// Set display property to inline-block for height/width
// animations on inline elements that are having width/height animated
// 对于内联元素,当动画 width 或 height 时,将 display 属性设置为 inline-block
if ( jQuery.css( this, "display" ) === "inline" &&
jQuery.css( this, "float" ) === "none" ) {
// inline-level elements accept inline-block;
// block-level elements need to be inline with layout
if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
// 内联元素使用 inline-block块级元素需要 inline 布局
if (!jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
this.style.display = "inline-block";
} else {
this.style.zoom = 1;
}
@ -8593,424 +8593,530 @@
}
}
if ( opt.overflow != null ) {
// 如果 opt.overflow 不为 null将元素的 overflow 属性设置为 "hidden"
if ( opt.overflow!= null ) {
this.style.overflow = "hidden";
}
for ( p in prop ) {
// 创建 jQuery.fx 对象
e = new jQuery.fx( this, opt, p );
val = prop[ p ];
if ( rfxtypes.test( val ) ) {
// Tracks whether to show or hide based on private
// data attached to the element
method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 );
// 根据元素的私有数据来决定是显示还是隐藏
method = jQuery._data( this, "toggle" + p ) || ( val === "toggle"? hidden? "show" : "hide" : 0 );
if ( method ) {
jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" );
// 存储 toggle 数据
jQuery._data( this, "toggle" + p, method === "show"? "hide" : "show" );
// 调用相应的 show 或 hide 方法
e[ method ]();
} else {
// 调用 val 对应的方法
e[ val ]();
}
} else {
parts = rfxnum.exec( val );
start = e.cur();
if ( parts ) {
// 获取最终值和单位
end = parseFloat( parts[2] );
unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
unit = parts[3] || ( jQuery.cssNumber[ p ]? "" : "px" );
// We need to compute starting value
if ( unit !== "px" ) {
// 计算起始值
if ( unit!== "px" ) {
jQuery.style( this, p, (end || 1) + unit);
start = ( (end || 1) / e.cur() ) * start;
jQuery.style( this, p, start + unit);
}
// If a +=/-= token was provided, we're doing a relative animation
// 如果有 += 或 -= 标记,进行相对动画
if ( parts[1] ) {
end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
end = ( (parts[ 1 ] === "-="? -1 : 1) * end ) + start;
}
// 调用 custom 方法进行自定义动画
e.custom( start, end, unit );
} else {
// 调用 custom 方法进行自定义动画,使用默认单位
e.custom( start, val, "" );
}
}
}
// For JS strict compliance
// 为了符合 JS 严格模式
return true;
}
return optall.queue === false ?
// 根据 optall.queue 是否为 false 决定执行动画的方式
return optall.queue === false?
this.each( doAnimation ) :
this.queue( optall.queue, doAnimation );
},
stop: function( type, clearQueue, gotoEnd ) {
if ( typeof type !== "string" ) {
// 如果 type 不是字符串,重新分配参数
if ( typeof type!== "string" ) {
gotoEnd = clearQueue;
clearQueue = type;
type = undefined;
}
if ( clearQueue && type !== false ) {
// 如果 clearQueue 为真且 type 不为 false将相应队列清空
if ( clearQueue && type!== false ) {
this.queue( type || "fx", [] );
}
// 对集合中的每个元素进行操作
return this.each(function() {
var index,
hadTimers = false,
timers = jQuery.timers,
data = jQuery._data( this );
// clear marker counters if we know they won't be
if ( !gotoEnd ) {
// 如果不跳转到结束,清除标记计数器
if (!gotoEnd ) {
jQuery._unmark( true, this );
}
// 定义 stopQueue 函数,用于停止队列
function stopQueue( elem, data, index ) {
var hooks = data[ index ];
// 移除元素上的 index 数据
jQuery.removeData( elem, index, true );
// 调用 hooks 的 stop 方法,可能是停止动画
hooks.stop( gotoEnd );
}
// 如果 type 为 null
if ( type == null ) {
// 遍历 data 中的每个属性
for ( index in data ) {
// 如果属性存在且有 stop 方法且 index 以.run 结尾
if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) {
// 调用 stopQueue 函数
stopQueue( this, data, index );
}
}
} else if ( data[ index = type + ".run" ] && data[ index ].stop ){
// 如果存在 type + ".run" 且有 stop 方法,调用 stopQueue 函数
stopQueue( this, data, index );
}
// 遍历定时器数组
for ( index = timers.length; index--; ) {
// 如果定时器元素是当前元素且满足类型条件
if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
if ( gotoEnd ) {
// force the next step to be the last
// 强制下一个步骤为最后一步
timers[ index ]( true );
} else {
// 保存定时器状态
timers[ index ].saveState();
}
hadTimers = true;
// 从定时器数组中移除该定时器
timers.splice( index, 1 );
}
}
// start the next in the queue if the last step wasn't forced
// timers currently will call their complete callbacks, which will dequeue
// but only if they were gotoEnd
if ( !( gotoEnd && hadTimers ) ) {
// 如果没有强制最后一步,开始队列中的下一个任务
if (!( gotoEnd && hadTimers ) ) {
jQuery.dequeue( this, type );
}
});
}
});
// Animations created synchronously will run synchronously
// 创建当前动画时间的函数
function createFxNow() {
setTimeout( clearFxNow, 0 );
return ( fxNow = jQuery.now() );
// 立即调用 clearFxNow 函数,延迟时间为 0
setTimeout(clearFxNow, 0);
// 调用 jQuery.now 函数获取当前时间并赋值给 fxNow同时将结果返回
return (fxNow = jQuery.now());
}
// 清除 fxNow 变量的函数
function clearFxNow() {
// 将 fxNow 设为 undefined
fxNow = undefined;
}
// Generate parameters to create a standard animation
function genFx( type, num ) {
// 生成创建标准动画的参数的函数
function genFx(type, num) {
var obj = {};
jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
obj[ this ] = type;
// 使用 jQuery.each 方法遍历 fxAttrs 数组及其中的元素
jQuery.each(fxAttrs.concat.apply([], fxAttrs.slice(0, num)), function () {
// 将对象的属性设置为传入的 type
obj[this] = type;
});
// 返回生成的对象
return obj;
}
// Generate shortcuts for custom animations
// 为自定义动画生成快捷方式
jQuery.each({
slideDown: genFx( "show", 1 ),
slideUp: genFx( "hide", 1 ),
slideToggle: genFx( "toggle", 1 ),
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 );
// 生成 slideDown 动画的参数
slideDown: genFx("show", 1),
// 生成 slideUp 动画的参数
slideUp: genFx("hide", 1),
// 生成 slideToggle 动画的参数
slideToggle: genFx("toggle", 1),
// 生成 fadeIn 动画的参数,将 opacity 属性设置为 show
fadeIn: {opacity: "show"},
// 生成 fadeOut 动画的参数,将 opacity 属性设置为 hide
fadeOut: {opacity: "hide"},
// 生成 fadeToggle 动画的参数,将 opacity 属性设置为 toggle
fadeToggle: {opacity: "toggle"}
}, function (name, props) {
// 为 jQuery.fn 对象添加相应的方法,例如 slideDown、slideUp 等
jQuery.fn[name] = function (speed, easing, callback) {
// 调用 animate 方法执行动画
return this.animate(props, speed, easing, callback);
};
});
jQuery.extend({
speed: function( speed, easing, fn ) {
var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
complete: fn || !fn && easing ||
jQuery.isFunction( speed ) && speed,
// 定义 speed 函数,用于处理动画速度相关的配置
speed: function (speed, easing, fn) {
// 如果 speed 是对象,则扩展一个空对象并赋值给 opt否则根据 speed、easing 和 fn 来创建 opt 对象
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
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;
// 如果 jQuery.fx.off 为真,则持续时间设为 0否则根据不同情况设置持续时间
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;
// normalize opt.queue - true/undefined/null -> "fx"
if ( opt.queue == null || opt.queue === true ) {
// 规范化 opt.queue 的值,将 true、undefined 或 null 规范化为 "fx"
if (opt.queue == null || opt.queue === true) {
opt.queue = "fx";
}
// Queueing
// 队列操作
opt.old = opt.complete;
opt.complete = function( noUnmark ) {
if ( jQuery.isFunction( opt.old ) ) {
opt.old.call( this );
// 重新定义 complete 函数
opt.complete = function (noUnmark) {
// 如果 opt.old 是函数,调用它
if (jQuery.isFunction(opt.old)) {
opt.old.call(this);
}
if ( opt.queue ) {
jQuery.dequeue( this, opt.queue );
} else if ( noUnmark !== false ) {
jQuery._unmark( this );
// 如果存在队列,执行出队操作
if (opt.queue) {
jQuery.dequeue(this, opt.queue);
} else if (noUnmark!== false) {
// 否则执行 _unmark 操作
jQuery._unmark(this);
}
};
// 返回配置对象 opt
return opt;
},
// 定义 easing 函数,包含线性和摆动的缓动函数
easing: {
linear: function( p ) {
// 线性缓动函数,直接返回输入的参数 p
linear: function (p) {
return p;
},
swing: function( p ) {
return ( -Math.cos( p*Math.PI ) / 2 ) + 0.5;
// 摆动缓动函数,根据输入的参数 p 进行计算
swing: function (p) {
return (-Math.cos(p * Math.PI) / 2) + 0.5;
}
},
// 存储定时器的数组
timers: [],
fx: function( elem, options, prop ) {
// 定义 fx 函数,用于创建一个 fx 对象
fx: function (elem, options, prop) {
// 将传入的选项赋值给 this.options
this.options = options;
// 将传入的元素赋值给 this.elem
this.elem = elem;
// 将传入的属性赋值给 this.prop
this.prop = prop;
// 确保 options.orig 存在,如果不存在则初始化为空对象
options.orig = options.orig || {};
}
});
jQuery.fx.prototype = {
// Simple function for setting a style value
// 更新函数,用于更新样式的值
update: function() {
if ( this.options.step ) {
this.options.step.call( this.elem, this.now, this );
// 如果存在 options.step 函数,调用它并将 this.elem 和 this 作为参数传递
if (this.options.step) {
this.options.step.call(this.elem, this.now, this);
}
( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
// 调用 jQuery.fx 中对应的 step 函数,如果没有对应属性的 step 函数,则调用默认的 step 函数
(jQuery.fx.step[this.prop] || jQuery.fx.step._default)(this);
},
// Get the current size
// 获取当前大小的函数
cur: function() {
if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {
return this.elem[ this.prop ];
// 如果元素的属性值不为空且元素没有样式或者元素的样式中该属性为空
if (this.elem[this.prop]!= null && (!this.elem.style || this.elem.style[this.prop] == null)) {
// 返回元素的该属性值
return this.elem[this.prop];
}
var parsed,
r = jQuery.css( this.elem, this.prop );
// 使用 jQuery.css 函数获取元素该属性的样式值
r = jQuery.css(this.elem, this.prop);
// Empty strings, null, undefined and "auto" are converted to 0,
// complex values such as "rotate(1rad)" are returned as is,
// simple values such as "10px" are parsed to Float.
return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
// 将解析后的结果存储在 parsed 中,如果解析结果是 NaN 且 r 为空或为 "auto" 则返回 0否则返回 r否则返回解析结果
return isNaN(parsed = parseFloat(r))? (!r || r === "auto"? 0 : r) : parsed;
},
// Start an animation from one number to another
custom: function( from, to, unit ) {
// 从一个数开始到另一个数开始动画的函数
custom: function(from, to, unit) {
var self = this,
fx = jQuery.fx;
// 记录动画开始时间,使用 fxNow 或 createFxNow 函数获取
this.startTime = fxNow || createFxNow();
// 结束值设置为 to
this.end = to;
// 初始的当前值和起始值设置为 from
this.now = this.start = from;
this.pos = this.state = 0;
this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
// 设置单位,如果传入了 unit 则使用 unit否则使用默认的单位对于 jQuery.cssNumber 中存在的属性不使用单位,其他情况使用 "px"
this.unit = unit || this.unit || (jQuery.cssNumber[this.prop]? "" : "px");
function t( gotoEnd ) {
return self.step( gotoEnd );
// 定义内部函数 t调用 self.step 函数
function t(gotoEnd) {
return self.step(gotoEnd);
}
// 为 t 函数添加 queue 属性,设置为 this.options.queue
t.queue = this.options.queue;
// 为 t 函数添加 elem 属性,设置为 this.elem
t.elem = this.elem;
// 为 t 函数添加 saveState 方法
t.saveState = function() {
if ( jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) {
if ( self.options.hide ) {
jQuery._data( self.elem, "fxshow" + self.prop, self.start );
} else if ( self.options.show ) {
jQuery._data( self.elem, "fxshow" + self.prop, self.end );
// 如果元素上没有存储 "fxshow" + self.prop 数据
if (jQuery._data(self.elem, "fxshow" + self.prop) === undefined) {
// 如果是隐藏操作,存储起始值
if (self.options.hide) {
jQuery._data(self.elem, "fxshow" + self.prop, self.start);
} else if (self.options.show) {
// 如果是显示操作,存储结束值
jQuery._data(self.elem, "fxshow" + self.prop, self.end);
}
}
};
if ( t() && jQuery.timers.push(t) && !timerId ) {
timerId = setInterval( fx.tick, fx.interval );
// 调用 t 函数,如果结果为真且将 t 函数添加到 jQuery.timers 数组中且 timerId 不存在
if (t() && jQuery.timers.push(t) &&!timerId) {
// 启动定时器,调用 fx.tick 函数,时间间隔为 fx.interval
timerId = setInterval(fx.tick, fx.interval);
}
},
}
};
// Simple 'show' function
show: function() {
// 获取元素上存储的 "fxshow" + this.prop 数据
var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );
// Remember where we started, so that we can go back to it later
// 记住开始的状态,以便之后可以恢复,将其存储在 options.orig[this.prop] 中,如果 dataShow 存在则使用 dataShow否则使用元素的当前属性值
this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
// 标记为显示操作
this.options.show = true;
// Begin the animation
// Make sure that we start at a small width/height to avoid any flash of content
if ( dataShow !== undefined ) {
// This show is picking up where a previous hide or show left off
// 开始动画
// 确保开始时使用较小的宽度/高度以避免内容闪烁
if ( dataShow!== undefined ) {
// 这个显示操作是从之前的隐藏或显示操作中断的地方继续
this.custom( this.cur(), dataShow );
} else {
this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() );
// 根据属性是否为宽度或高度,设置不同的起始值
this.custom( this.prop === "width" || this.prop === "height"? 1 : 0, this.cur() );
}
// Start by showing the element
// 首先显示元素
jQuery( this.elem ).show();
},
// Simple 'hide' function
// 简单的 'hide' 函数
hide: function() {
// Remember where we started, so that we can go back to it later
// 记住开始的状态,以便之后可以恢复,将其存储在 options.orig[this.prop] 中,如果存储的数据存在则使用存储的数据,否则使用元素的当前属性值
this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop );
// 标记为隐藏操作
this.options.hide = true;
// Begin the animation
// 开始动画
this.custom( this.cur(), 0 );
},
// Each step of an animation
// 动画的每一步
step: function( gotoEnd ) {
var p, n, complete,
// 获取当前时间,若 fxNow 存在则使用 fxNow否则调用 createFxNow 方法获取
t = fxNow || createFxNow(),
done = true,
elem = this.elem,
options = this.options;
// 如果 gotoEnd 为真或当前时间大于等于持续时间加开始时间
if ( gotoEnd || t >= options.duration + this.startTime ) {
// 将 this.now 设置为结束值
this.now = this.end;
// 将 this.pos 和 this.state 都设置为 1
this.pos = this.state = 1;
// 执行更新操作
this.update();
// 标记当前属性的动画已完成
options.animatedProperties[ this.prop ] = true;
// 遍历 animatedProperties 中的每个属性
for ( p in options.animatedProperties ) {
if ( options.animatedProperties[ p ] !== true ) {
// 如果该属性的动画未完成
if ( options.animatedProperties[ p ]!== true ) {
// 标记为未完成
done = false;
}
}
}
if ( done ) {
// Reset the overflow
if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
// 如果 done 为 true
// 重置溢出属性
if ( options.overflow!= null &&!jQuery.support.shrinkWrapBlocks ) {
// 使用 jQuery.each 方法遍历数组 ["", "X", "Y"]
jQuery.each( [ "", "X", "Y" ], function( index, value ) {
// 将 elem 的 overflow 属性及其变体overflowX、overflowY设置为 options.overflow 数组中相应索引的值
elem.style[ "overflow" + value ] = options.overflow[ index ];
});
}
// Hide the element if the "hide" operation was done
// 如果是执行了 "hide" 操作,将元素隐藏
if ( options.hide ) {
jQuery( elem ).hide();
}
// Reset the properties, if the item has been hidden or shown
// 如果执行了 "hide" 或 "show" 操作,重置相关属性
if ( options.hide || options.show ) {
// 遍历 options.animatedProperties 对象的属性
for ( p in options.animatedProperties ) {
// 将元素的属性 p 重置为 options.orig[p] 的值
jQuery.style( elem, p, options.orig[ p ] );
// 移除元素上的 "fxshow" + p 数据
jQuery.removeData( elem, "fxshow" + p, true );
// Toggle data is no longer needed
// 移除元素上的 "toggle" + p 数据
jQuery.removeData( elem, "toggle" + p, true );
}
}
// Execute the complete function
// in the event that the complete function throws an exception
// we must ensure it won't be called twice. #5684
// 执行完成函数
// 若完成函数抛出异常,确保它不会被调用两次,解决 #5684 问题
complete = options.complete;
if ( complete ) {
// 将 options.complete 设为 false
options.complete = false;
// 以 elem 作为上下文调用 complete 函数
complete.call( elem );
}
}
// 返回 false
return false;
} else {
// classical easing cannot be used with an Infinity duration
// 如果 duration 为 Infinity经典的缓动效果无法使用
if ( options.duration == Infinity ) {
// 将 this.now 设置为 t
this.now = t;
} else {
// 计算动画已运行的时间 n
n = t - this.startTime;
// 计算动画的状态(已运行时间 / 总时长)
this.state = n / options.duration;
// Perform the easing function, defaults to swing
// 执行缓动函数,默认使用 swing 函数
this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );
// 计算当前的动画值
this.now = this.start + ( (this.end - this.start) * this.pos );
}
// Perform the next step of the animation
// 执行动画的下一步更新
this.update();
}
// 返回 true
return true;
}
};
// 使用 jQuery.extend 方法扩展 jQuery.fx 对象
jQuery.extend( jQuery.fx, {
// 定义 tick 函数
tick: function() {
// 定义 timer 变量和 timers 数组,以及索引 i 并初始化为 0
var timer,
timers = jQuery.timers,
i = 0;
// 遍历 timers 数组
for ( ; i < timers.length; i++ ) {
// 获取当前的定时器
timer = timers[ i ];
// Checks the timer has not already been removed
if ( !timer() && timers[ i ] === timer ) {
// 检查定时器尚未被移除,如果定时器函数的返回值为 false 且 timers[i] 仍然是当前的 timer
if (!timer() && timers[ i ] === timer ) {
// 从 timers 数组中移除该定时器,并将索引 i 减 1
timers.splice( i--, 1 );
}
}
if ( !timers.length ) {
// 如果 timers 数组的长度为 0
if (!timers.length ) {
// 调用 jQuery.fx.stop 方法
jQuery.fx.stop();
}
},
// 定义动画的时间间隔为 13 毫秒
interval: 13,
// 定义 stop 函数
stop: function() {
// 清除定时器
clearInterval( timerId );
// 将 timerId 设为 null
timerId = null;
},
// 定义速度对象,包含 slow、fast 和默认速度
speeds: {
slow: 600,
fast: 200,
// Default speed
// 默认速度为 400 毫秒
_default: 400
},
// 定义 step 对象,包含不同动画效果的处理函数
step: {
// 处理透明度的动画函数
opacity: function( fx ) {
// 使用 jQuery.style 方法设置元素的透明度为 fx.now 的值
jQuery.style( fx.elem, "opacity", fx.now );
},
// 默认的动画处理函数
_default: function( fx ) {
if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
// 如果元素的样式存在且元素的样式属性存在
if ( fx.elem.style && fx.elem.style[ fx.prop ]!= null ) {
// 设置元素的样式属性值为 fx.now + fx.unit
fx.elem.style[ fx.prop ] = fx.now + fx.unit;
} else {
// 否则设置元素的属性值为 fx.now
fx.elem[ fx.prop ] = fx.now;
}
}
@ -9036,136 +9142,192 @@
}
// Try to restore the default display value of an element
function defaultDisplay( nodeName ) {
if ( !elemdisplay[ nodeName ] ) {
function defaultDisplay(nodeName) {
// 检查 elemdisplay 中是否不存在以 nodeName 为键的值
if (!elemdisplay[nodeName]) {
// 获取文档的 body 元素
var body = document.body,
elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
display = elem.css( "display" );
// 创建一个 jQuery 对象,元素为 <nodeName> 并添加到 body 中
elem = jQuery("<" + nodeName + ">").appendTo(body),
// 获取元素的 display 样式
display = elem.css("display");
// 移除该元素
elem.remove();
// If the simple way fails,
// get element's real default display by attaching it to a temp iframe
if ( display === "none" || display === "" ) {
// No iframe to use yet, so create it
if ( !iframe ) {
iframe = document.createElement( "iframe" );
// 如果简单的方式失败,通过将元素附加到临时 iframe 来获取元素真正的默认显示
if (display === "none" || display === "") {
// 如果还没有 iframe创建一个 iframe 元素
if (!iframe) {
iframe = document.createElement("iframe");
// 设置 iframe 的 frameBorder、width 和 height 为 0
iframe.frameBorder = iframe.width = iframe.height = 0;
}
body.appendChild( iframe );
// 将 iframe 添加到 body 中
body.appendChild(iframe);
// Create a cacheable copy of the iframe document on first call.
// IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
// document to it; WebKit & Firefox won't allow reusing the iframe document.
if ( !iframeDoc || !iframe.createElement ) {
iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
iframeDoc.write( ( jQuery.support.boxModel ? "<!doctype html>" : "" ) + "<html><body>" );
// 在第一次调用时创建可缓存的 iframe 文档副本
// IE 和 Opera 允许我们重用 iframeDoc 而无需重写假 HTML 文档WebKit 和 Firefox 不允许重用 iframe 文档
if (!iframeDoc ||!iframe.createElement) {
// 获取 iframe 的文档对象
iframeDoc = (iframe.contentWindow || iframe.contentDocument).document;
// 向 iframe 文档中写入 HTML 内容
iframeDoc.write((jQuery.support.boxModel? "<!doctype html>" : "") + "<html><body>");
// 关闭 iframe 文档的写入操作
iframeDoc.close();
}
elem = iframeDoc.createElement( nodeName );
// 在 iframe 文档中创建元素
elem = iframeDoc.createElement(nodeName);
iframeDoc.body.appendChild( elem );
// 将元素添加到 iframe 文档的 body 中
iframeDoc.body.appendChild(elem);
display = jQuery.css( elem, "display" );
body.removeChild( iframe );
// 获取元素的 display 样式
display = jQuery.css(elem, "display");
// 从 body 中移除 iframe
body.removeChild(iframe);
}
// Store the correct default display
elemdisplay[ nodeName ] = display;
// 存储正确的默认显示
elemdisplay[nodeName] = display;
}
return elemdisplay[ nodeName ];
// 返回 elemdisplay 中存储的元素的默认显示
return elemdisplay[nodeName];
}
// 定义变量 getOffsetrtable 是一个正则表达式,用于匹配以 t 开头,后面跟 able、d 或 h 的字符串,不区分大小写
var getOffset,
rtable = /^t(?:able|d|h)$/i,
// rroot 是一个正则表达式,用于匹配 body 或 html 字符串,不区分大小写
rroot = /^(?:body|html)$/i;
if ( "getBoundingClientRect" in document.documentElement ) {
getOffset = function( elem, doc, docElem, box ) {
// 检查 document.documentElement 是否有 getBoundingClientRect 方法
if ("getBoundingClientRect" in document.documentElement) {
// 定义 getOffset 函数,接收 elem, doc, docElem, box 作为参数
getOffset = function (elem, doc, docElem, box) {
try {
// 尝试获取元素的边界矩形信息
box = elem.getBoundingClientRect();
} catch(e) {}
} catch (e) {}
// Make sure we're not dealing with a disconnected DOM node
if ( !box || !jQuery.contains( docElem, elem ) ) {
return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
// 确保元素不是一个断开连接的 DOM 节点
if (!box ||!jQuery.contains(docElem, elem)) {
// 如果 box 存在则返回包含 top 和 left 的对象,否则返回 top 和 left 为 0 的对象
return box? { top: box.top, left: box.left } : { top: 0, left: 0 };
}
// 获取 body 元素
var body = doc.body,
win = getWindow( doc ),
clientTop = docElem.clientTop || body.clientTop || 0,
// 获取窗口对象
win = getWindow(doc),
// 获取 docElem 的 clientTop 或 body 的 clientTop若都没有则为 0
clientTop = docElem.clientTop || body.clientTop || 0,
// 获取 docElem 的 clientLeft 或 body 的 clientLeft若都没有则为 0
clientLeft = docElem.clientLeft || body.clientLeft || 0,
scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop,
// 获取页面垂直滚动距离
scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop,
// 获取页面水平滚动距离
scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
top = box.top + scrollTop - clientTop,
// 计算元素的 top 位置
top = box.top + scrollTop - clientTop,
// 计算元素的 left 位置
left = box.left + scrollLeft - clientLeft;
// 返回包含元素最终计算得到的 top 和 left 位置的对象
return { top: top, left: left };
};
} else {
// 定义一个名为getOffset的函数用于计算元素的偏移量
getOffset = function( elem, doc, docElem ) {
// 定义 getOffset 函数,接收 elem, doc, docElem 作为参数
getOffset = function (elem, doc, docElem) {
var computedStyle,
// 获取元素的 offsetParent
offsetParent = elem.offsetParent,
// 存储上一个 offsetParent
prevOffsetParent = elem,
// 获取 body 元素
body = doc.body,
// 获取文档的默认视图
defaultView = doc.defaultView,
prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
// 获取元素的计算样式
prevComputedStyle = defaultView? defaultView.getComputedStyle(elem, null) : elem.currentStyle,
// 获取元素的垂直偏移量
top = elem.offsetTop,
// 获取元素的水平偏移量
left = elem.offsetLeft;
while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
// 循环,从元素的父节点开始,直到 body 或 docElem 为止
while ((elem = elem.parentNode) && elem!== body && elem!== docElem) {
// 如果 jQuery 支持 fixedPosition 且元素的位置是 fixed则跳出循环
if (jQuery.support.fixedPosition && prevComputedStyle.position === "fixed") {
break;
}
computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
top -= elem.scrollTop;
// 获取元素父节点的计算样式
computedStyle = defaultView? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
// 从元素的垂直偏移量中减去父节点的滚动距离
top -= elem.scrollTop;
// 从元素的水平偏移量中减去父节点的滚动距离
left -= elem.scrollLeft;
if ( elem === offsetParent ) {
top += elem.offsetTop;
// 如果当前节点是元素的 offsetParent
if (elem === offsetParent) {
// 加上父节点的垂直偏移量
top += elem.offsetTop;
// 加上父节点的水平偏移量
left += elem.offsetLeft;
if ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
top += parseFloat( computedStyle.borderTopWidth ) || 0;
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
// 如果 jQuery 不添加边框且元素不是 table 或其单元格
if (jQuery.support.doesNotAddBorder &&!(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName))) {
// 加上父节点的上边框宽度
top += parseFloat(computedStyle.borderTopWidth) || 0;
// 加上父节点的左边框宽度
left += parseFloat(computedStyle.borderLeftWidth) || 0;
}
// 更新 prevOffsetParent
prevOffsetParent = offsetParent;
// 更新 offsetParent
offsetParent = elem.offsetParent;
}
if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
top += parseFloat( computedStyle.borderTopWidth ) || 0;
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
// 如果 jQuery 支持减去不可见溢出元素的边框且元素的 overflow 不是 visible
if (jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow!== "visible") {
// 加上父节点的上边框宽度
top += parseFloat(computedStyle.borderTopWidth) || 0;
// 加上父节点的左边框宽度
left += parseFloat(computedStyle.borderLeftWidth) || 0;
}
// 更新 prevComputedStyle
prevComputedStyle = computedStyle;
}
if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
top += body.offsetTop;
// 如果元素的位置是 relative 或 static
if (prevComputedStyle.position === "relative" || prevComputedStyle.position === "static") {
// 加上 body 的垂直偏移量
top += body.offsetTop;
// 加上 body 的水平偏移量
left += body.offsetLeft;
}
if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
top += Math.max( docElem.scrollTop, body.scrollTop );
left += Math.max( docElem.scrollLeft, body.scrollLeft );
// 如果 jQuery 支持 fixedPosition 且元素的位置是 fixed
if (jQuery.support.fixedPosition && prevComputedStyle.position === "fixed") {
// 加上 docElem 或 body 的垂直滚动距离
top += Math.max(docElem.scrollTop, body.scrollTop);
// 加上 docElem 或 body 的水平滚动距离
left += Math.max(docElem.scrollLeft, body.scrollLeft);
}
// 返回包含元素最终计算得到的 top 和 left 位置的对象
return { top: top, left: left };
};
};
}
// 重写jQuery的fn.offset方法允许对jQuery对象进行偏移量设置或获取
jQuery.fn.offset = function( options ) {
// 判断是否有传入参数,即是否要设置偏移量

Loading…
Cancel
Save