如jQuery般易用的api風(fēng)格代碼分享
回到正題,如jQuery般易用的api風(fēng)格?那到底是什么樣的風(fēng)格呢?個(gè)人覺(jué)得比較重要的有兩點(diǎn),一是對(duì)dom操作的鏈?zhǔn)秸{(diào)用,并且都呈隊(duì)列狀態(tài),不僅僅使代碼的可讀語(yǔ)義變得通俗易懂,而且省去了對(duì)同一dom元素的多個(gè)鏈?zhǔn)讲僮鲿r(shí)的回調(diào)中嵌回調(diào)的方式,這是很重要的一點(diǎn)。
二是對(duì)元素的批量操作,這是建立在它強(qiáng)大的選擇器上的。jq選擇器很強(qiáng)大,這是人所眾知的,就不多說(shuō)了。而且肯定也不是一兩天就能實(shí)現(xiàn)的了的,所以下面就對(duì)我所說(shuō)的這兩點(diǎn)談?wù)勎业目捶ā?
基于它強(qiáng)大的選擇器,jquery所有的dom操作都依賴于根據(jù)它選擇器取到的一個(gè)數(shù)組,很多人喜歡把這叫做jq對(duì)象。那咱們暫時(shí)也先這樣叫吧。然后所有的dom操作都是依賴于這個(gè)jq對(duì)象中每一個(gè)元素并發(fā)批量執(zhí)行的。具體到每一個(gè)dom操作,大部分的都是呈鏈?zhǔn)交卣{(diào)的狀態(tài),也就是說(shuō)在這個(gè)方法鏈里面,直接根據(jù)方法調(diào)用在鏈中的先后順序就能知道他們執(zhí)行的順序。這種方法鏈并且串行的形式是它的一大特色。
以至于很多人喜歡用jquery,基本就看中它兩點(diǎn),選擇器確實(shí)很強(qiáng)大,鏈?zhǔn)秸{(diào)用確實(shí)很方便很易用,代碼邏輯瞬間變得簡(jiǎn)單。正因?yàn)樗押芏嗟拇a邏輯都放到自己內(nèi)部去處理了,留給編碼者考慮的問(wèn)題就少了很多,所以一方面你覺(jué)得好用的同時(shí),也就失去了一次鍛煉編碼邏輯的機(jī)會(huì)。因此我不建議初學(xué)者直接學(xué)習(xí)使用jquery或者其他的框架,因?yàn)樗麄儠?huì)讓你對(duì)js的理解越來(lái)越少。我的觀點(diǎn)是所有的框架或者庫(kù)都是拿來(lái)使用的,拿來(lái)提高開(kāi)發(fā)效率和管理便利度的,而不是拿來(lái)學(xué)習(xí)的。(當(dāng)然,研究源碼的除外)。
那么,既然覺(jué)得jquery的api風(fēng)格好用,那我們何嘗不嘗試一下構(gòu)建這種類似的api風(fēng)格呢?(聲明:以下嘗試都僅僅是提供一種思路,代碼并不完善...)
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]));
}
}
}
然后為它擴(kuò)展一些操作dom的方法
get.prototype = {
each : function () {},
animate : function () {}
}
當(dāng)然,這種方式和jQuery看起來(lái)不太一樣,但能理解就行,jquery可能是這個(gè)樣子:
jQuery = window.jQuery = window.$ = function( selector, context ) {
return new jQuery.fn.init( selector, context );
}
jQuery.fn = jQuery.prototype = {
init: function( selector, context ) {}
}
接下來(lái)對(duì)獲取的隊(duì)列進(jìn)行批量操作,不可避免的就需要一個(gè)each的遍歷方法。
each : function (fn) {
for (var i=0; i<this.elements.length; i++) {
fn.call(this, this.elements[i])
}
return this;
},
each為get.prototype擴(kuò)展出的方法,提供一個(gè)參數(shù)function,并且遍歷dom列表,把function綁定到每一個(gè)元素上。然后讓它返回get.prototype,因?yàn)閜rototype本身具有類似于“超類”的性質(zhì),所以凡是返回給prototype對(duì)象的方法都能繼續(xù)調(diào)用prototype擴(kuò)展出來(lái)到方法。
為了使這個(gè)嘗試更有意義一點(diǎn),接下來(lái)來(lái)做一個(gè)animate的函數(shù)吧。這個(gè)函數(shù)是jquery對(duì)dom操作很常用的一個(gè)方法,有了它,大部分的動(dòng)畫(huà)都變得那么簡(jiǎn)單和容易了。下面會(huì)是一個(gè)簡(jiǎn)單的實(shí)現(xiàn):
animate: function (config) {
if (!this.animQueue) this.animQueue = HR._animQueue = [];
var a = 0, time, tween, ease, callback;
while (arguments[++a]) {
if (typeof arguments[a] == 'number') time = arguments[a];
if (typeof arguments[a] == 'string') {
if (/^ease*/.test(arguments[a])) ease = arguments[a];
else tween = arguments[a];
}
if (HR.isFunction(arguments[a])) callback = arguments[a];
}
this.animQueue.push({
config: config,
time: time,
tween: tween,
ease: ease,
callback: callback
});
if (this.animQueue.length == 1) this.execute(this.animQueue);
return this;
},
光看這一段可能看不出什么端倪,是的,因?yàn)橐駄query一樣做成串行的方法鏈,就需要一個(gè)臨時(shí)隊(duì)列來(lái)操作,要不然即使方法鏈形成了,但這些方法都是并行的,達(dá)不到我們想要的效果。所以上面一段代碼主要是處理animate推入隊(duì)列的一個(gè)邏輯,然后對(duì)參數(shù)arguments做了一些判斷,以便在寫(xiě)參數(shù)的時(shí)候能更加隨意,除了第一個(gè)參數(shù)和最后一個(gè)callback外,其余參數(shù)不用考慮位置和是否必填,以增強(qiáng)易用性。
核心的變換函數(shù)在execute上,
execute : function (queue) {
var _this = this, m = 0, n = 0,
_anim = function (el, key, from, to, at, tw, ease, cb) {
var isOP = (key == 'opacity' && !HR.support.opacity), _key = key;
if (isOP) {to = to*100; _key = 'filter'}
var s = +new Date,
d = at,
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.execute(_this.animQueue);
}
return;
}
el.style[_key] = (isOP ? 'alpha(opacity=' : '') + Tween[tw][ease](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{1,3}/g)[0]) : HR.getStyle(k, el),
_q.config[k],
typeof _q.time == 'number' ? _q.time : 1000,
typeof _q.tween == 'string' && !/^ease*/.test(_q.tween) ? _q.tween : 'Quart',
typeof _q.ease == 'string' && /^ease*/.test(_q.ease) ? _q.ease : 'easeOut',
_q.callback)
}
});
}
這一段看起來(lái)就要復(fù)雜一些了,最基本的變化還是在_anim這個(gè)私有函數(shù)上。其余的代碼基本在做一些批量的操作,和透明度變化兼容性,以及當(dāng)前變換是否執(zhí)行完畢的功能。結(jié)合這兩段,基本就實(shí)現(xiàn)了jquery的animate的效果了。屬于一個(gè)簡(jiǎn)化版本。
當(dāng)然,還不能忘了很重要的一點(diǎn),就是既然可以變換,那就必須有個(gè)stop的方法讓這個(gè)變換可控,要不然這個(gè)代碼的可用性會(huì)大打折扣,參考以下代碼:
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;
},
針對(duì)不同的dom元素id設(shè)置專門(mén)的臨時(shí)計(jì)時(shí)器存貯,HR.timers[el.id],然后遍歷當(dāng)前dom列表,把對(duì)應(yīng)的計(jì)時(shí)器clear掉。參數(shù)clearQueue作為可選參數(shù),用來(lái)控制是否清掉后續(xù)等待執(zhí)行的animate。
為了讓這個(gè)方法更加好玩一點(diǎn),我加了幾種額外的緩動(dòng)方式,jquery只有一種swing,然后所有的緩動(dòng)算法放置在Tween對(duì)象中以供使用。下面是我做測(cè)試的源碼,(如有紕漏,各位見(jiàn)諒)
/* =========== 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 = { // 以下算子的參數(shù)分別表示: t:運(yùn)行時(shí)間,b:開(kāi)始量,c:總變化量,d:總時(shí)間
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*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
}
},
Cubic: {
easeIn: function(t,b,c,d){
return c*(t/=d)*t*t + b;
},
easeOut: function(t,b,c,d){
return c*((t=t/d-1)*t*t + 1) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
}
},
Quart: {
easeIn: function(t,b,c,d){
return c*(t/=d)*t*t*t + b;
},
easeOut: function(t,b,c,d){
return -c * ((t=t/d-1)*t*t*t - 1) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
return -c/2 * ((t-=2)*t*t*t - 2) + b;
}
},
Quint: {
easeIn: function(t,b,c,d){
return c*(t/=d)*t*t*t*t + b;
},
easeOut: function(t,b,c,d){
return c*((t=t/d-1)*t*t*t*t + 1) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
return c/2*((t-=2)*t*t*t*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/a);
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/a);
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/a);
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*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*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, ease, callback;
while (arguments[++a]) {
if (typeof arguments[a] == 'number') time = arguments[a];
if (typeof arguments[a] == 'string') {
if (/^ease*/.test(arguments[a])) ease = arguments[a];
else tween = arguments[a];
}
if (HR.isFunction(arguments[a])) callback = arguments[a];
}
this.animQueue.push({
config: config,
time: time,
tween: tween,
ease: ease,
callback: callback
});
if (this.animQueue.length == 1) this.execute(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, ease, cb) {
var isOP = (key == 'opacity' && !HR.support.opacity), _key = key;
if (isOP) {to = to*100; _key = 'filter'}
var s = +new Date,
d = at,
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.execute(_this.animQueue);
}
return;
}
el.style[_key] = (isOP ? 'alpha(opacity=' : '') + Tween[tw][ease](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{1,3}/g)[0]) : HR.getStyle(k, el),
_q.config[k],
typeof _q.time == 'number' ? _q.time : 1000,
typeof _q.tween == 'string' && !/^ease*/.test(_q.tween) ? _q.tween : 'Quart',
typeof _q.ease == 'string' && /^ease*/.test(_q.ease) ? _q.ease : '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 : {}
});
})();
然后為了讓大家看的直觀一點(diǎn),小做了兩個(gè)demo
【demo1】
[Ctrl+A 全選 注:引入外部Js需再刷新一下頁(yè)面才能執(zhí)行]
【demo2】
[Ctrl+A 全選 注:引入外部Js需再刷新一下頁(yè)面才能執(zhí)行]
- jQuery EasyUI API 中文幫助文檔和擴(kuò)展實(shí)例
- Jquery api 速查表分享
- 不同的jQuery API來(lái)處理不同的瀏覽器事件
- jQuery EasyUI API 中文文檔 - TreeGrid 樹(shù)表格使用介紹
- jQuery EasyUI API 中文文檔 - Tree樹(shù)使用介紹
- jQuery EasyUI API 中文文檔 - PropertyGrid屬性表格
- jQuery EasyUI API 中文文檔 - DataGrid數(shù)據(jù)表格
- jQuery EasyUI API 中文文檔 - Dialog對(duì)話框
- jQuery EasyUI API 中文文檔 - TimeSpinner時(shí)間微調(diào)器
- Dreamweaver jQuery智能提示插件,支持版本提示,支持1.6api
- jquery.combobox中文api和例子,修復(fù)了上面的小bug
- jQuery validate 中文API 附validate.js中文api手冊(cè)
- jQuery EasyUI 中文API Layout(Tabs)
- jQuery EasyUI 中文API Button使用實(shí)例
- jquery 1.4.2發(fā)布!主要是性能與API
- jquery中有哪些api jQuery主要API
相關(guān)文章
JS?中在嚴(yán)格模式下?this?的指向問(wèn)題
這篇文章主要介紹了JS?中在嚴(yán)格模式下this的指向問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09
JS+CSS實(shí)現(xiàn)的漂亮漸變背景特效代碼(6個(gè)漸變效果)
這篇文章主要介紹了JS+CSS實(shí)現(xiàn)的漂亮漸變背景特效代碼,包含6個(gè)漸變效果,涉及JavaScript針對(duì)頁(yè)面元素屬性動(dòng)態(tài)操作的相關(guān)技巧,需要的朋友可以參考下2016-03-03
ES6學(xué)習(xí)筆記之Set和Map數(shù)據(jù)結(jié)構(gòu)詳解
這篇文章主要介紹了ES6學(xué)習(xí)筆記之Set和Map數(shù)據(jù)結(jié)構(gòu),結(jié)合實(shí)例形式詳細(xì)分析了ECMAScript中基本數(shù)據(jù)結(jié)構(gòu)Set和Map的常用屬性與方法的功能、用法及相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-04-04
通過(guò)JS來(lái)判斷頁(yè)面控件是否獲取焦點(diǎn)
本篇文章主要介紹了通過(guò)JS來(lái)判斷頁(yè)面控件是否獲取焦點(diǎn)的方法。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-01-01
JS+Canvas實(shí)現(xiàn)的俄羅斯方塊游戲完整實(shí)例
這篇文章主要介紹了JS+Canvas實(shí)現(xiàn)的俄羅斯方塊游戲,結(jié)合完整實(shí)例形式分析了Canvas技術(shù)實(shí)現(xiàn)俄羅斯方塊游戲的步驟、技術(shù)難點(diǎn)與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-12-12
如何在TypeScript使用模塊化以及注意事項(xiàng)詳解
在TypeScript中就像在EC5中一樣,任何包含頂級(jí)import或export的文件都被認(rèn)為是一個(gè)模塊,下面這篇文章主要給大家介紹了關(guān)于如何在TypeScript使用模塊化以及注意事項(xiàng)的相關(guān)資料,需要的朋友可以參考下2022-10-10
JavaScript 對(duì)象模型 執(zhí)行模型
簡(jiǎn)單數(shù)值類型: 有Undefined, Null, Boolean, Number和String。注意,描述中的英文單詞在這里僅指數(shù)據(jù)類型的名稱,并不特指JS的全局對(duì)象N an, Boolean, Number, String等,它們?cè)诟拍钌系膮^(qū)別是比較大的。2010-10-10

