Back to the topic, is jQuery an easy-to-use api style? What is the style? I personally think there are two important points: first, the chain calls to dom operations are in the queue state, not only to make the code readable semantics easy to understand, in addition, the callback method embedded in the callback for multiple chain operations on the same dom element is removed, which is very important.
Second, batch operations on elements are built on its powerful selector. The jq selector is very powerful, which is well known to all. And it certainly won't be possible in a day or two, so I will share my views on these two points.
Based on its powerful selector, all dom operations of jquery depend on an array obtained from its selector. Many people like to call this a jq object. Let's call it this way for the moment. Then, all dom operations depend on the concurrent batch execution of each element in the jq object. For each dom operation, most of the operations are chained Callbacks. That is to say, in this method chain, the execution sequence of method calls can be known directly according to the sequence of method calls in the chain. This method chain and serial form is a major feature of it.
So many people like to use jquery. Basically, they just take a fancy to its two points. The selector is really powerful, the chain call is really convenient and easy to use, and the code logic instantly becomes simple. Because he put a lot of code logic into his own system to deal with it, there will be fewer issues for the coders to consider, so on the one hand you think it is easy to use at the same time, it also loses a chance to exercise the encoding logic. Therefore, I do not recommend that beginners learn to use jquery or other frameworks directly, because they will give you less and less understanding of js. My point is that all frameworks or libraries are used to improve development efficiency and management convenience, rather than learning. (Except for source code ).
So, since jquery's api style is easy to use, why don't we try to build this similar api style? (Declaration: The following attempts only provide an idea that the Code is not complete ...)
Copy codeThe Code is as follows: var get = function (ids ){
Var d = document, a =-1;
This. elements = [];
If (typeof ids! = 'String '&&!! Ids. length ){
For (var I = 0; I <ids. length; I ++ ){
Var id = ids [I], o;
O = typeof id = 'string '? D. getElementById (id): id;
This. elements. push (o );
}
} Else {
While (typeof arguments [++ a] = 'string '){
This. elements. push (d. getElementById (arguments [a]);
}
}
}
Then extend some dom operations for it.Copy codeThe Code is as follows: get. prototype = {
Each: function (){},
Animate: function (){}
}
Of course, this method does not look the same as jQuery, But you can understand it. jquery may look like this:Copy codeThe Code is as follows: jQuery = window. jQuery = window. $ = function (selector, context ){
Return new jQuery. fn. init (selector, context );
}
JQuery. fn = jQuery. prototype = {
Init: function (selector, context ){}
}
Next, Perform Batch operations on the obtained queues. An each Traversal method is required.Copy codeThe Code is as follows: each: function (fn ){
For (var I = 0; I <this. elements. length; I ++ ){
Fn. call (this, this. elements [I])
}
Return this;
},
Each provides a function parameter for the get. prototype extension method, traverses the dom list, and binds the function to each element. Then let it return get. prototype. Because prototype itself has a property similar to "superclass", all methods returned to the prototype object can continue to call prototype to expand to the method.
To make this attempt more meaningful, let's create an animation function. This function is a commonly used method of jquery for dom operations. With it, most animations become so simple and easy. The following is a simple implementation:Copy codeThe Code is as follows: animate: function (config ){
If (! This. animQueue) this. animQueue = HR. _ animQueue = [];
Var a = 0, time, tween, callback, callback;
While (arguments [++ a]) {
If (typeof arguments [a] = 'number') time = arguments [a];
If (typeof arguments [a] = 'string '){
If (/^ parameters */. test (arguments [a]) parameters = arguments [a];
Else tween = arguments [a];
}
If (HR. isFunction (arguments [a]) callback = arguments [a];
}
This. animQueue. push ({
Config: config,
Time: time,
Tween: tween,
Metadata: Metadata,
Callback: callback
});
If (this. animQueue. length = 1) this.exe cute (this. animQueue );
Return this;
},
Looking at this section may not show any clue. Yes, because a serial method chain like jquery requires a temporary queue to operate, or even if the method chain is formed, however, these methods are parallel and cannot achieve the desired effect. Therefore, the above Code mainly deals with the logic of pushing animate into the queue, and then makes some judgments on the parameter arguments so that it can be more casual when writing parameters, except for the first parameter and the last callback parameter, you do not need to consider the location and whether it is required to enhance ease of use.
The core transform function is on execute,Copy codeThe Code is as follows: execute: function (queue ){
Var _ this = this, m = 0, n = 0,
_ Anim = function (el, key, from, to, at, tw, clerk, cb ){
Var isOP = (key = 'opacity '&&! HR. support. opacity), _ key = key;
If (isOP) {to = to * 100; _ key = 'filter '}
Var s = + new Date,
D =,
B = parseFloat (from) | 0,
C = to-B;
(Function (){
Var t = + new Date-s;
If (t> = d ){
N ++;
T = d;
El. style [_ key] = (isOP? 'Alpha (opacity = ': '') + Tween. Linear (t, B, c, d) + (key! = 'Opacity '? 'Px ': '') + (isOP? ')':'');
!! Cb & cb. apply (el );
If (m = n & _ this. animQueue. length> 1 ){
_ This. animQueue. shift ();
_This.exe cute (_ this. animQueue );
}
Return;
}
El. style [_ key] = (isOP? 'Alpha (opacity = ': '') + Tween [tw] [Keys] (t, B, c, d) + (key! = 'Opacity '? 'Px ': '') + (isOP? ')':'');
If (! HR. timers [el. id]) HR. timers [el. id] = [];
HR. timers [el. id]. push (setTimeout (arguments. callee, 16 ));
})();
},
_ Q = this. animQueue [0];
Return this. each (function (el ){
For (var k in _ q. config ){
M ++;
_ Anim (el,
K,
K = 'opacity '&&! HR. support. opacity? HR. getStyle ('filter', el) = ''? 100: parseInt (HR. getStyle ('filter', el). match (/\ d {}/g) [0]): HR. getStyle (k, el ),
_ Q. config [k],
Typeof _ q. time = 'number '? -Q. time: 1000,
Typeof _ q. tween = 'string '&&! /^ Messages */. test (_ q. tween )? _ Q. tween: 'quart ',
Typeof _ q. Keys = 'string' &/^ tables */. test (_ q. Keys )? _ Q. Outputs: 'easeout ',
_ Q. callback)
}
});
}
This section looks complicated. The most basic change is the private function _ anim. The rest of the code is basically performing batch operations, and is compatible with transparency changes, and whether the current transformation has been executed. Combined with these two sections, the effects of jquery's animate are basically achieved. Is a simplified version.
Of course, you can't forget the very important thing: Since the change can be done, you must have a stop method to make the change controllable. Otherwise, the availability of the code will be greatly reduced, refer to the following code:Copy codeThe Code is as follows: stop: function (clearQueue ){
If (clearQueue) HR. _ animQueue. length = 0;
This. each (function (el ){
If (!! HR. timers [el. id])
For (var I = 0; I <HR. timers [el. id]. length; I ++) clearTimeout (HR. timers [el. id] [I])
});
Return this;
},
Set a dedicated temporary timer for storing different dom element IDs, HR. timers [el. id], traverse the current dom list, and clear the corresponding timer. The clearQueue parameter is an optional parameter used to determine whether to clear the animation waiting for execution.
To make this method more fun, I have added several additional easing methods. jquery only has one swing type, and all the easing algorithms are placed in the Tween object for use. The following is the source code for testing. (If any, please forgive me)Copy codeThe Code is as follows:/* ============= animate js ================= */
/* @ Author: hongru. chen */
/* = */
If (typeof HR = 'undefined' |! HR)
HR = {
Extend: function (destination, source, override ){
If (override ==# ff0000) override = true;
For (var property in source ){
If (override |! (Property in destination )){
Destination [property] = source [property];
}
}
Return destination;
}
};
(Function (){
Var Tween = {// parameters of the following operators are represented by t: Run Time, B: Start volume, c: total variation volume, and d: Total time.
Linear: function (t, B, c, d) {return c * t/d + B ;},
Quad :{
EaseIn: function (t, B, c, d ){
Return c * (t/= d) * t + B;
},
EaseOut: function (t, B, c, d ){
Return-c * (t/= d) * (t-2) + B;
},
EaseInOut: function (t, B, c, d ){
If (t/= d/2) <1) return c/2 * t + B;
Return-c/2 * (-- t) * (t-2)-1) + B;
}
},
Cubic :{
EaseIn: function (t, B, c, d ){
Return c * (t/= d) * t + B;
},
EaseOut: function (t, B, c, d ){
Return c * (t = t/D-1) * t + 1) + B;
},
EaseInOut: function (t, B, c, d ){
If (t/= d/2) <1) return c/2 * t + B;
Return c/2 * (t-= 2) * t + 2) + B;
}
},
Quart :{
EaseIn: function (t, B, c, d ){
Return c * (t/= d) * t + B;
},
EaseOut: function (t, B, c, d ){
Return-c * (t = t/D-1) * t-1) + B;
},
EaseInOut: function (t, B, c, d ){
If (t/= d/2) <1) return c/2 * t + B;
Return-c/2 * (t-= 2) * t-2) + B;
}
},
Quint :{
EaseIn: function (t, B, c, d ){
Return c * (t/= d) * t + B;
},
EaseOut: function (t, B, c, d ){
Return c * (t = t/D-1) * t + 1) + B;
},
EaseInOut: function (t, B, c, d ){
If (t/= d/2) <1) return c/2 * t + B;
Return c/2 * (t-= 2) * t + 2) + B;
}
},
Sine :{
EaseIn: function (t, B, c, d ){
Return-c * Math. cos (t/d * (Math. PI/2) + c + B;
},
EaseOut: function (t, B, c, d ){
Return c * Math. sin (t/d * (Math. PI/2) + B;
},
EaseInOut: function (t, B, c, d ){
Return-c/2 * (Math. cos (Math. PI * t/d)-1) + B;
}
},
Expo :{
EaseIn: function (t, B, c, d ){
Return (t = 0 )? B: c * Math. pow (2, 10 * (t/d-1) + B;
},
EaseOut: function (t, B, c, d ){
Return (t = d )? B + c: c * (-Math. pow (2,-10 * t/d) + 1) + B;
},
EaseInOut: function (t, B, c, d ){
If (t = 0) return B;
If (t = d) return B + c;
If (t/= d/2) <1) return c/2 * Math. pow (2, 10 * (t-1) + B;
Return c/2 * (-Math. pow (2,-10 * -- t) + 2) + B;
}
},
Circ :{
EaseIn: function (t, B, c, d ){
Return-c * (Math. sqrt (1-(t/= d) * t)-1) + B;
},
EaseOut: function (t, B, c, d ){
Return c * Math. sqrt (1-(t = t/D-1) * t) + B;
},
EaseInOut: function (t, B, c, d ){
If (t/= d/2) <1) return-c/2 * (Math. sqrt (1-t * t)-1) + B;
Return c/2 * (Math. sqrt (1-(t-= 2) * t) + 1) + B;
}
},
Elastic :{
EaseIn: function (t, B, c, d, a, p ){
If (t = 0) return B; if (t/= d) = 1) return B + c; if (! P) p = d *. 3;
If (! A | a <Math. abs (c) {a = c; var s = p/4 ;}
Else var s = p/(2 * Math. PI) * Math. asin (c/);
Return-(a * Math. pow (2,10 * (t-= 1) * Math. sin (t * d-s) * (2 * Math. PI)/p) + B;
},
EaseOut: function (t, B, c, d, a, p ){
If (t = 0) return B; if (t/= d) = 1) return B + c; if (! P) p = d *. 3;
If (! A | a <Math. abs (c) {a = c; var s = p/4 ;}
Else var s = p/(2 * Math. PI) * Math. asin (c/);
Return (a * Math. pow (2,-10 * t) * Math. sin (t * d-s) * (2 * Math. PI)/p) + c + B );
},
EaseInOut: function (t, B, c, d, a, p ){
If (t = 0) return B; if (t/= d/2) = 2) return B + c; if (! P) p = d * (. 3*1.5 );
If (! A | a <Math. abs (c) {a = c; var s = p/4 ;}
Else var s = p/(2 * Math. PI) * Math. asin (c/);
If (t <1) return -. 5 * (a * Math. pow (2,10 * (t-= 1) * Math. sin (t * d-s) * (2 * Math. PI)/p) + B;
Return a * Math. pow (2,-10 * (t-= 1) * Math. sin (t * d-s) * (2 * Math. PI)/p )*. 5 + c + B;
}
},
Back :{
EaseIn: function (t, B, c, d, s ){
If (s = undefined) s = 1.70158;
Return c * (t/= d) * t * (s + 1) * t-s) + B;
},
EaseOut: function (t, B, c, d, s ){
If (s = undefined) s = 1.70158;
Return c * (t = t/D-1) * t * (s + 1) * t + s) + 1) + B;
},
EaseInOut: function (t, B, c, d, s ){
If (s = undefined) s = 1.70158;
If (t/= d/2) <1) return c/2 * (t * (s * = (1.525) + 1) * t-s) + B;
Return c/2 * (t-= 2) * t * (s * = (1.525) + 1) * t + s) + 2) + B;
}
},
Bounce :{
EaseIn: function (t, B, c, d ){
Return c-Tween. Bounce. easeOut (d-t, 0, c, d) + B;
},
EaseOut: function (t, B, c, d ){
If (t/= d) <(1/2. 75 )){
Return c * (7.5625 * t) + B;
} Else if (t <(2/2. 75 )){
Return c * (7.5625 * (t-= (1.5/2.75) * t ++ 75) + B;
} Else if (t <(2.5/2.75 )){
Return c * (7.5625 * (t-= (2.25/2.75) * t +. 9375) + B;
} Else {
Return c * (7.5625 * (t-= (2.625/2.75) * t +. 984375) + B;
}
},
EaseInOut: function (t, B, c, d ){
If (t <d/2) return Tween. Bounce. easeIn (t * 2, 0, c, d) *. 5 + B;
Else return Tween. Bounce. easeOut (t * 2-d, 0, c, d) *. 5 + c *. 5 + B;
}
}
}
Var get = function (ids ){
Var d = document, a =-1;
This. elements = [];
If (typeof ids! = 'String '&&!! Ids. length ){
For (var I = 0; I <ids. length; I ++ ){
Var id = ids [I], o;
O = typeof id = 'string '? D. getElementById (id): id;
This. elements. push (o );
}
} Else {
While (typeof arguments [++ a] = 'string '){
This. elements. push (d. getElementById (arguments [a]);
}
}
}
Get. prototype = {
Each: function (fn ){
For (var I = 0; I <this. elements. length; I ++ ){
Fn. call (this, this. elements [I])
}
Return this;
},
SetStyle: function (p, v ){
This. each (function (el ){
El. style [p] = v;
});
Return this;
},
Show: function (){
Var _ this = this;
This. each (function (el ){
_ This. setStyle ('display', 'block ');
})
Return this;
},
Hide: function (){
Var _ this = this;
This. each (function (el ){
_ This. setStyle ('display', 'None ');
})
Return this;
},
Animate: function (config ){
If (! This. animQueue) this. animQueue = HR. _ animQueue = [];
Var a = 0, time, tween, callback, callback;
While (arguments [++ a]) {
If (typeof arguments [a] = 'number') time = arguments [a];
If (typeof arguments [a] = 'string '){
If (/^ parameters */. test (arguments [a]) parameters = arguments [a];
Else tween = arguments [a];
}
If (HR. isFunction (arguments [a]) callback = arguments [a];
}
This. animQueue. push ({
Config: config,
Time: time,
Tween: tween,
Metadata: Metadata,
Callback: callback
});
If (this. animQueue. length = 1) this.exe cute (this. animQueue );
Return this;
},
Stop: function (clearQueue ){
If (clearQueue) HR. _ animQueue. length = 0;
This. each (function (el ){
If (!! HR. timers [el. id])
For (var I = 0; I <HR. timers [el. id]. length; I ++) clearTimeout (HR. timers [el. id] [I])
});
Return this;
},
Execute: function (queue ){
Var _ this = this, m = 0, n = 0,
_ Anim = function (el, key, from, to, at, tw, clerk, cb ){
Var isOP = (key = 'opacity '&&! HR. support. opacity), _ key = key;
If (isOP) {to = to * 100; _ key = 'filter '}
Var s = + new Date,
D =,
B = parseFloat (from) | 0,
C = to-B;
(Function (){
Var t = + new Date-s;
If (t> = d ){
N ++;
T = d;
El. style [_ key] = (isOP? 'Alpha (opacity = ': '') + Tween. Linear (t, B, c, d) + (key! = 'Opacity '? 'Px ': '') + (isOP? ')':'');
!! Cb & cb. apply (el );
If (m = n & _ this. animQueue. length> 1 ){
_ This. animQueue. shift ();
_This.exe cute (_ this. animQueue );
}
Return;
}
El. style [_ key] = (isOP? 'Alpha (opacity = ': '') + Tween [tw] [Keys] (t, B, c, d) + (key! = 'Opacity '? 'Px ': '') + (isOP? ')':'');
If (! HR. timers [el. id]) HR. timers [el. id] = [];
HR. timers [el. id]. push (setTimeout (arguments. callee, 16 ));
})();
},
_ Q = this. animQueue [0];
Return this. each (function (el ){
For (var k in _ q. config ){
M ++;
_ Anim (el,
K,
K = 'opacity '&&! HR. support. opacity? HR. getStyle ('filter', el) = ''? 100: parseInt (HR. getStyle ('filter', el). match (/\ d {}/g) [0]): HR. getStyle (k, el ),
_ Q. config [k],
Typeof _ q. time = 'number '? -Q. time: 1000,
Typeof _ q. tween = 'string '&&! /^ Messages */. test (_ q. tween )? _ Q. tween: 'quart ',
Typeof _ q. Keys = 'string' &/^ tables */. test (_ q. Keys )? _ Q. Outputs: 'easeout ',
_ Q. callback)
}
});
}
}
HR. extend (HR ,{
Get: function (){
Return new get (arguments );
},
IsFunction: function (o ){
Return typeof (o) = 'function '&&(! Function. prototype. call | typeof (o. call) = 'function ');
},
GetStyle: function (p, el ){
Return el. currentStyle? El. currentStyle [p]: document. defaultView. getComputedStyle (el, null). getPropertyValue (p );
},
Support: (function (){
Try {
Var d = document. createElement ('div ');
D. style ['display'] = 'none ';
D. innerHTML = '<a style = "float: left; opacity:. 5;"> </a> ';
Var a = d. getElementsByTagName ('A') [0];
Return {
Opacity: a. style. opacity = '0. 5'
}
} Finally {
D = null;
}
})(),
Timers :{}
});
})();
Then we made two demos to make everyone look more intuitive.
[Demo1]<Meta http-equiv = "Content-Type" content = "text/html; charset = UTF-8"> <br/> <style type = "text/css"> # me, # you {border: 2px solid #333; margin-bottom: 10px; position: absolute; overflow: hidden ;}# me {left: 0; top: 0} # you {left: 0; top: 300px }</style> fasdf <br/> fasdffasdf <br/> fasdf <p> <input type = "button" value = "stop" style = "margin-top: 600px "onclick =" HR. get ('me', 'you '). stop (). animate ({top: 100}) ">