1 jQuery.extend({ 2 // 显示或操作在匹配元素上执行的函数队列 3 queue: function (elem, type, data) { 4 var queue; 5 6 if (elem) { 7 // type没定义的话就默认使用fxqueue 8 type = (type || 'fx') + 'queue'; 9 // 先获取elem上的缓存数据 10 queue = jQuery._data(elem, type); 11 12 if (data) { 13 // 如果没有缓存数据或者data是数组 14 // 则给elem设置缓存数据 15 if (!queue || jQuery.isArray(data)) { 16 queue = jQuery._data(elem, type, jQuery.makeArray(data)); 17 } else { 18 // 否则存在缓存数据数组直接入栈 19 queue.push(data); 20 } 21 } 22 return queue || []; 23 } 24 }, 25 // 从队列最前端移除一个队列函数,并执行它 26 dequeue: function (elem, type) { 27 type = type || 'fx'; 28 29 var 30 // 获取缓存队列 31 queue = jQuery.queue(elem, type), 32 startLength = queue.length, 33 // 取得队列的第一个元素 34 fn = queue.shift(), 35 // 钩子对象,如果获取不到缓存的钩子对象,就设置新的钩子对象并返回 36 hooks = jQuery._queueHooks(elem, type), 37 // 用来执行下一个队列 38 next = function () { 39 jQuery.dequeue(elem, type); 40 }; 41 42 // 如果第一个元素是"inprogress",取下一个元素,长度相应减1 43 if (fn === 'inprogress') { 44 fn = queue.shift(); 45 startLength--; 46 } 47 48 // 为钩子对象添加cur方法 49 hooks.cur = fn; 50 // 如果此时fn不为null || false || undefined 51 if (fn) { 52 // 如果type为“fx”,给队列添加"inprogress",防止自动出列(dequeue) 53 if (type === 'fx') { 54 queue.unshift('inprogress'); 55 } 56 57 // 删除上一个队列的stop函数 58 delete hooks.stop; 59 // 执行fn函数,并且把下一个队列函数设置为fn的第一个参数 60 /* 61 可以这样使用: 62 $(document.body).queue('test', function(next){ 63 console.log(11); 64 next(); 65 }); 66 $(document.body).queue('test', function(){ 67 console.log(22); 68 }); 69 $(document.body).dequeue('test'); 70 */ 71 fn.call(elem, next, hooks); 72 } 73 74 // 如果队列长度为0且存在钩子对象,则删除缓存 75 if (!startLength && hooks) { 76 hooks.empty.fire(); 77 } 78 }, 79 // 私有方法,生成一个队列钩子对象(即从缓存数据中获取的队列钩子) 80 // 或者设置缓存队列钩子对象,这个对象的empty属性是一个Callbacks对象,这里的作用是删除缓存队列和缓存队列钩子的数据 81 _queueHooks: function (elem, type) { 82 var key = type + 'queueHooks'; 83 return jQuery._data(elem, key) || jQuery._data(elem, key, { 84 empty: jQuery.Callbacks('once memory').add(function () { 85 jQuery._removeData(elem, type + 'queue'); 86 jQuery._removeData(elem, key); 87 }) 88 }); 89 } 90 }); 91 92 jQuery.fn.extend({ 93 queue: function (type, data) { 94 var setter = 2; 95 96 if (typeof type !== 'string') { 97 data = type; 98 type = 'fx'; 99 setter--; 100 } 101 102 /* 103 当满足这个条件的有以下几个情况: 104 1.没有参数 105 2.参数只有一个,且type是字符串 106 */ 107 if (arguments.length < setter) { 108 return jQuery.queue(this[0], type); 109 } 110 111 // 其他情况 112 return data === undefined ? 113 this : 114 this.each(function () { 115 var queue = jQuery.queue(this, type, data); 116 117 jQuery._queueHooks(this, type); 118 119 // 如果是动画队列且第一个元素不是"inprogres", 120 // 就出列并执行下一个元素 121 if (type === 'fx' && queue[0] !== 'inprogress') { 122 jQuery.dequeue(this, type); 123 } 124 }); 125 }, 126 dequeue: function (type) { 127 return this.each(function () { 128 jQuery.dequeue(this, type); 129 }); 130 }, 131 // TODO 132 delay: function (time, type) { 133 time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; 134 type = type || 'fx'; 135 136 return this.queue(type, function (next, hooks) { 137 var timeout = setTimeout(next, time); 138 hooks.stop = function () { 139 clearTimeout(timeout); 140 }; 141 }); 142 }, 143 clearQueue: function (type) { 144 return this.queue(type || 'fx', []); 145 }, 146 promise: function (type, obj) { 147 var tmp, 148 count = 1, 149 defer = jQuery.Deferred(), 150 elements = this, 151 i = this.length, 152 resolve = function () { 153 if (!(--count)) { 154 defer.resolveWith(elements, [elements]); 155 } 156 }; 157 158 if (typeof type !== 'string') { 159 obj = type; 160 type = undefined; 161 } 162 type = type || 'fx'; 163 164 while (i--) { 165 tmp = jQuery._data(elements[i], type + 'queueHooks'); 166 if (tmp && tmp.empty) { 167 count++; 168 tmp.empty.add(resolve); 169 } 170 } 171 resolve(); 172 return defer.promise(obj); 173 } 174 });