顯示具有 javascript 標籤的文章。 顯示所有文章
顯示具有 javascript 標籤的文章。 顯示所有文章

2015年2月18日 星期三

Deferred 與 Promise 的差異


在談Promise 之前,我們先來了解一下 CommonJS.org

CommonJS.org

CommonJS 是一個致力於將 JavaScript 生態圈標準化的計畫,尤其是針對瀏覽器以外的應用環境,也因此第一個版本的名字叫做 ServerJS。
CommonJS 的終極目標是制定一個像 C++ 標準庫一樣的規範,也就是介面,使得基於 CommonJS API 的應用程序可以在不同的環境下運行,就像用 C++ 編寫的應用程序可以使用不同的編譯器和運行時函數庫一樣。為了保持中立,CommonJS 不參與函式庫的實作,而是交給像 Node.js 之類的項目來完成。

而不少開發者向 CommonJS 提出 Promise 的規範草案,但是一直未被列入正式規範,有著 Promises/A, B, KISS, C, D 等版本。其中以 Promises/A 的提案最為簡單,也最常被大家提及。

值得一提的是:Promise/B, D 的提案人是知名流程控管套件 Q.js 的作者 Kris Kowal
滿有趣的一點是:Node 團隊在前年左右放棄繼續遵守 CommonJS 的規範。Isaac 指出 "Ryan basically always gave zero fucks about CommonJS anyway" ,並轉述了 Ryan 的一句重話 : "Forget CommonJS. It's dead. We are server side JavaScript."
有興趣可以看看這個Github的討論串:https://github.com/joyent/node/issues/5132#issuecomment-15432598
( Issac 是 NPM 的作者, Ryan 是 Node 之父 )

2015年2月4日 星期三

你還在為了UI-binding使用肥肥的Angular嗎?試試這些前端函式庫吧!

從 jQuery 到 Angular

從最早的 jQuery 以後,各種前端framework百花齊放:Backbone, Knockout, Ember, Angular....
其中 Angular 狹著 Google 的大名,可以說是在前端世界中呼風喚雨,但是許多人使用Anugular一段時間以後才發現入門容易但是深入難,而且隨著專案的複雜性增加,也開始踩到了各種地雷。因此也有人用這張圖來揶揄模擬Angular的學習曲線:



UI only

許多人開始發現,很多狀態下他們幾乎只需要 Data-UI binding 的功能,React.js 這個 facebook 御用、注重高效render 的 Library 就開始受到廣大的目光關注... React不是個完整的framework,只是專注於View的Library,因此更輕量、也更能與其他framework整合、相容。React官網是這麼說的:
Just the UI
Lots of people use React as the V in MVC. Since React makes no assumptions about the rest of your technology stack, it's easy to try it out on a small feature in an existing project.


但是!

React 推薦使用 JSX 在程式碼中勾勒 template,這點我就真的不行了,覺得髒髒的。所以我實在無法接受 React。

但是幸好這是一個前端技術大爆炸的時代,我們還有很多選擇....


2013年4月13日 星期六

[JS] javascript OO設計 - 繼承

接觸javascript有一定的人應該都知道在js中,你所看到的幾乎都是物件。
函數是物件的第一型,Function和Object都是物件,也是函數的實例,而所有的function都是Function的instance。

關於原形(prototype)

prototype是函數物件特有的屬性,使用函數建構出來的物件會藉由原型鏈(prototype chain)而存取得到建構函數的prototype的屬性,用文字說清楚很難,不如我們直接看範例:
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.nation = "Taiwan";
var kevin = new Person("Kevin", "18");
var laura = new Person("Laura", "17");
alert(kevin.nation); // "Taiwan"
alert(laura.nation); // "Taiwan"
雖然kevin和laura本身並沒有被定義"nation"這個屬性,但是當使用這些不存在的屬性時,會找他們的建構函數中的prototype,再沒有的話還會看這個prototype物件的建構函數中有沒有這個屬性,直到最高層,也就是Object建構函數的prototype,這就是所謂的prototype chain。為了證明這點,我們再次修改一下上面這個例子:
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.nation = "Taiwan";
Function.prototype.whatTypeAmI = "Funciton";
Object.prototype.whatTypeAmI = "Object";
var kevin = new Person("Kevin", "18");
alert(kevin.nation); // "Taiwan"
alert(kevin.whatTypeAmI); // "Object"
alert(Person.whatTypeAmI); // "Function"

從上面可以看出雖然kevin這個物件沒有whatTypeAmI這個屬性,但是他會往上找Person.prototype物件有沒有這個屬性,再找不到就會找Person.prototype的建構函式,也就是預設的Object函式,這時我們會在他的prototype中找到whatTypeAmI這個屬性是"Object"。
而我們在呼叫Person的whatTypeAmI時也會發生找不到的情形,這時就會往上找到他的建構函數也就是Funciton的prototype,如範例所示,這時回傳的是"Function"。


利用prototype來實踐javascript繼承模式

我們可以利用上面所提到的prototype chain來實踐多層祖孫繼承的模式,我們用範例來解釋一下這點:
// 哺乳綱
function Mammals() {
    this.blood = "warm";
}

// 靈長目
function Primate() {
    this.tail = true;
    this.skin = "hairy";
}
Primate.prototype = new Mammals();

// 人科
function Homo() {
    this.skin = "smooth";
}
Homo.prototype = new Primate();

var human = new Homo();
human.name = "Kevin";

alert(human.name); // "Kevin", from self
alert(human.skin); // "smooth", from Homo
alert(human.tail); // "true", from Primate
alert(human.blood); // "warm", from Mammals
上例應該很清楚的顯示出以原型實踐繼承模式的原理了。


原型設計模式的漏洞

雖然原型設計模式能夠很方便的實踐物件間的共用屬性及繼承模式,但是在操作上要對prototype chain有一定的了解,再加上細心的邏輯驗證,才不會出現如下的錯誤:
function Human() {}
Human.prototype.blood = "red";
Human.prototype.body = ["foot","hand"];

var john = new Human();
var kevin = new Human();

john.blood = "purple";
john.body.push("wing");

alert(kevin.blood); // "red"
alert(john.blood); // "purple"

alert(kevin.body.toString()); // "foot, hand, wing"
alert(kevin.body.toString()); // "foot, hand, wing"
從上面的例子可以看到,john因為不明原因而突變以後,不只血變成紫色的,也長出翅膀來了!但是在john突變之後,kevin的血雖然沒有變色,但是卻莫名其妙長出了翅膀。很明顯的,我們不小心改動到了Human的prototype。
原來在我們為john的blood指定顏色時,javascript會為john這個物件增加一個屬於自己的"blood"屬性,這種情況就跟為物件增加屬性的方式一樣。於是在後來的呼叫時,會先找到john自己的blood屬性。但要john的body屬性執行push函式時,會發生在john中找不到body的狀況,於是就往上找到了Human.prototype的body屬性,並由他來執行push函式,此時改動到的便是Human.prototype.body了,也就連帶的影響到了無辜的kevin。

其他繼承模式設計

javascript是個很活的語言,除了prototype的實踐方式以外,我們也可以使用別的方式來實現繼承:
// 哺乳綱
function Mammals() {
    this.blood = "warm";
}

// 靈長目
function Primate() {
    Mammals.call(this); // 記得放前面,不然會蓋掉重複的屬性
    this.tail = true;
    this.skin = "hairy";
}
Primate.prototype = new Mammals();

// 人科
function Homo() {
    Primate.call(this); // 記得放前面,不然會蓋掉重複的屬性
    this.skin = "smooth";
}

var human = new Homo();
human.name = "Kevin";

alert(human.name); // "Kevin", from self
alert(human.skin); // "smooth", from Homo
alert(human.tail); // "true", from Primate
alert(human.blood); // "warm", from Mammals
這種方式是將父類別的建構函式放在子類別的建構函式中以「this」的身分來執行,為自己建置父類別的屬性。這樣的作法有個好處,就是不會因為不當的操作,改動到別的物件的屬性,但是相對的也失去了共用屬性的便利性。

這種方式也能讓我們很方便的實作多重繼承,只要在子類別的建構函數中呼叫多個父類別的建構函式即可。

2013年2月16日 星期六

[JS] function - 函數物件

前言

javascript引擎要執行javascript時,會先"預編譯",即對所有宣告的變數及函式進行處理,但要注意的是,在函數中被宣告的local變數只有在函式被呼叫時才會被處理,函數return之後這些變數就會被釋放。


函數的定義

函數有三種定義方式:
1. 以function宣告(ex. function abc(){})
2. 匿名函數(ex.function(){})
3. Function建構函數(ex. var fn = new Function("return true;"))
前面有提到在預編譯時會先處理被宣告的函數及變數,所以第一種的宣告方式或是將匿名函數指定給一個變數時....
var fn = function(){/*Do something*/};
會在預編譯時期先被處理,但在直接執行匿名函數....
(function(){/*Do something*/})();
或是用Function建構函數來建立函數時都會在執行到該行程式碼的時候才會被處理。因為這樣的特性使得我們能用Function來比較動態的宣告函數,但因為必須以字串方式傳入函式的敘述內容(函式中要執行的程式碼),所以相對不好維護,而且因為是動態編譯的,效率也會較差,我們可以用一段程式碼來證明:
var startTime = (new Date()).getTime();
for(var i=0; i<100000; i++){
    function fn(){}
}
var total = (new Date()).getTime() - startTime;
alert("function*100,000 takes "+total); // 62 secs

var startTime = (new Date()).getTime();
for(var i=0; i<100000; i++){
    new Function();
}
var total = (new Date()).getTime() - startTime; // 18991 secs
alert("new Function*100,000 takes "+total);


函數的作用域(scope)

就像上一段所說,因為宣告function會在預編譯時期就被處理,所以函數的作用域是取決於宣告時所在的位置,立即執行函式(IIFE,直接執行匿名函數)也是。但是以Function建構函式所建立的函數作用域是Global。
var a = 1;
function fn(){
    var a=2;
    var fn1 = function(){alert(a);}
    var fn2 = new Function("alert(a)");
    fn1(); //"2"
    fn2(); //"1"
}
fn();


arguments物件

在function中可以使用arguments這個array物件,其中包含了所有被傳入此函數的參數,即使這個函數並不需要任何的輸入,例:
function fn(){
    for(var i in arguments){alert(arguments[i]);}
}
fn(1,2,3,4,5,6,7,8,9);
順帶一提,要如何取得一個函數預定接收幾個參數呢?可以使用function.length


.call() & .apply()

之前剛好寫了一篇關於這兩個方法的文章
這兩個方法能讓function很靈活的視作不同object的屬性來使用,切換不同的scope,用完之後馬上消失,不會真的成為該object的屬性。


別忘了function物件也有屬性

function也是個物件,當然也有自己的屬性。下面的這段程式碼想用迴圈來依次輸出1~10,但這樣的寫法用到了全域變數,缺乏封閉性。
function fn(){
    return x++;
} 
var x = 1;
for(var i=1; i<=10; i++){
    alert(fn());
}
所以我們可以利用函數自己的屬性來暫存這個值
function fn(){
    return fn.x++;
} 
fn.x = 1;
for(var i=1; i<=10; i++){
    alert(fn());
}


閉包(Closure)

在C、JAVA等語言中,當呼叫函數並傳回後,所有的區域變數就會被釋放,即刪除他們所在的堆疊。但在Javascript裡,如果宣告了一個內嵌函數,區域變數將在函數傳回後仍能被存取。我們可以利用這種特性來設計具有封閉及包裹性的閉包架構:
var mod = (function(){
    var d=0;
    var foo=function(){return d;};
    return foo;
})();
alert(mod()); // "0",還是存取得到閉包中的變數
關於Javascript的模組化可看一下之前的這篇文章


2013年2月12日 星期二

[JS] deferred.promise()的應用

今天在看jQuery的.animation()程式碼的時候注意到的。

之前就有稍微研究一下jQuery的deferred物件,今天看到他被用在animation的程式碼裡面,而且是我之前沒學過的用法,於是在這邊筆記一下。
var obj = { speak: function(name){alert(name);} }
    deferred = $.Deferred();
deferred.promise(obj);
obj.done(function(name){obj.speak(name)});
deferred.resolve("Kevin");

After being promised, the object would would be extended with some of the deferred object's function like "done", "fail", "always".

As the above, though the promised object can't resolve it self, but the deferred object who promise it can. And it can even pass an argument on resolving or rejecting it.

The deferred object really helps dealing with asyncronise problems in javascript.


2012年11月29日 星期四

[宅] 令我老淚縱橫的deferred物件

昨天參加完Ruby tuesday後不只解決了困擾已久的環境問題,還在聊天中得知了一個超好用的東西,就是jquery中的deferred物件.....雖然我LAG了很久但秉持著活到老學到老的精神,我還是要給他不知廉恥的寫下去。


事情是從jquery 1.5版開始的....


"老實說,我並不知道1.5版發生了什麼事情,但當我回過神來的時候,jquery的ajax函式已經開始回傳deferred物件了...."


jquery.Deferred(),或稱作$.Deferred(),是一個function,執行後會回傳一個Deferred物件:
Deferred: object
-->resolve: function
-->resolveWith: function
-->reject: function
-->rejectWith: function
-->notify: function
-->notifyWith: function
-->state: function
-->always: function
-->then: function
-->promise: function
-->pipe: function
-->done: function
-->fail: function
-->progress: function

這個物件和Ajax所回傳的object長得不太一樣(以下是get error的狀態):
deferred: object
-->abort: function
-->always: function
-->complete: function
-->done: function
-->error: function
-->fail: function
-->getAllResponseHeaders: function
-->getResponseHeader: function
-->pipe: function
-->progress: function
-->promise: function
-->readyState: 0
-->responseText: ""
-->setRequestHeader: function
-->state: function
-->status: 0
-->statusCode: function
-->statusText: "error"
-->success: function
-->then: function
黑字的部分都是deferred物件的function,但是明顯看得出比原生的deferred物件少了許多,我們姑且稱這是"閹割版"的deferred object,後面會再提到閹割版的由來。

用Ajax的結果來執行callback

Ajax回傳的物件中的"done"、"fail"函式,可以依成功與否來執行傳入的callback function,而很方便的是,這些函式是可以chain起來的。
$.get("http://google.com/").done(function(){alert("YES!!!");}).fail(function(){alert("NO!!!");});
這樣一來,成功的話就會alert "YES",失敗就alert "NO",而上面的寫法也可以用then()來做簡化:
$.get("http://google.com/").then(
    function(){alert("YES!!!");}, //成功時的callback
    function(){alert("NO!!!");} //失敗時的callback
)
但其實Ajax中本來就可以傳入"success"和"error"的callback function,所以光是上面的應用除了讓程式碼的可讀性變得更高也更好寫(當需要error callback時不用刻意改寫成ajax,仍舊可以使用get, getJSON, post)以外,似乎還是有點弱。
接著我們要介紹的是能讓deferred發揮得更強大的$.when()函式


用$.when( )解決"一堆"非同步AJAX的相依性問題

很多時候我們的程式碼會需要依賴數個AJAX回傳的資料來進行運算,以往我們可能只能用下列兩種解法:

  1. 改寫成同步AJAX,也就是一個一個接著執行
  2. 設一個值等於要AJAX次數的counter,每完成一個AJAX就減一,直到counter歸零就執行相依性的函式

但是第一種solution效率太差,第二種又太醜。這時我們就可以使用$.when( )
一般時候使用$.when()跟直接使用deferred物件是差不多的,因為$.when()也是回傳一個deferred物件:
var def = $.when($.ajax("test.html")); //deferred物件
$.when()真正強悍的地方在於能將數個AJAX的結果包成一個deferred物件,只要有一個失敗,那這個deffered的狀態就是"error",這樣講很抽象,我們直接看例子:
$.when( $.get("test1.html"), 
        $.post("test2.php"), 
        $.get("test3.html")
).then( doneCallback,
        failCallback    )
上面的三個AJAX只要有一個失敗了就會觸發errorCallback,三個都成功才會觸發successCallback。
這樣的寫法乾淨許多,可讀性也高。
延伸:AJAX的success callback與deferred的done callback 的執行順序
javascript是單執行緒的語言,但是常常會因為AJAX或wait這些常用的funciton讓初學者有所誤會。如果是非同步的情形,AJAX在等待遠端回覆的過程中,會直接往下執行,直到遠端回應了才會觸發success或error callback。
但是在同一時間javascript是只會做一件事的。所以在上面提到的例子中,如果數個AJAX有傳入success callback,當成功接收到respond時,AJAX會先呼叫success callback,就看哪個AJAX先收到respond而已。而因為javascript單執行緒的特性,在沒有ajax,timer,alert之類的情形下他會完完整整的把一個success callback執行完才執行下一個AJAX的callback。
所以上面的例子中,將會是三個AJAX的callback在不可預期的順序下執行完畢後,才會由$.when()回傳一個狀態為成功的deferred來觸發done callback function。不必擔心done callback有可能比AJAX的callback先被處理。


deferred不只能用在AJAX

如果其他函式也是非同步且具有相依性的需求,例如wait(),我們也可以利用deferred的設計思維來做相依性的處理。但是因為wait()並不回傳deferred物件,所以我們要再把這些函式包起來變成一個回傳deferred物件的函式。舉個例子:(轉載自阮一峰大大的部落格)
var wait = function(dtd){
  var tasks = function(){
    alert("执行完毕!");
    dtd.resolve(); // 改变Deferred对象的执行状态
  };

  setTimeout(tasks,5000);
  return dtd.promise(); // 返回promise对象
};
var d = wait(dtd); // 新建一个d对象,改为对这个对象进行操作
$.when(d)
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
d.resolve(); // 此时,这个语句是无效的
這個例子裡有兩個東西要注意,一個是"resolve()"、一個是"promise()"。
resolve()可以將deferred物件改為"成功"狀態並觸發done callback,相反的就是reject了。
promise()能將deferred物件"閹割"成文章最前面所呈現的樣子,這樣的物件沒有辦法再用resolve或reject來更改狀態,顧名思義就是"承諾"(promise)


2012年11月27日 星期二

[JS] function物件的call()&apply()

在javascript中,除了直接以function加上"()"來執行函式外,還可以呼叫function的call()或apply()來執行,但是"foo()"、"foo.call()"和"foo.apply()"其實是有差別的,接下來我們就來看看call()的用法:

function.prototype.call()

在browser上運行的javascript,其global scope是window,但當在不同地方呼叫global的function時,我也希望這個funciton能依據呼叫的所在而使用當時的scope,這時我們就能使用call。
以下是一段簡單的程式碼:
scope="window";
foo = function(){console.log(this.scope);};
cat={
    scope:"cat",
    foo:function(){console.log(this.scope);},
    bar:function(cb){cb();}
};

foo(); //"window"
cat.foo(); //"cat"

cat.bar(foo); //"window"
foo.call(cat); //"cat"
從這段簡短的程式碼中就可以看出call可以改變韓式執行的scope,這個功能常用在事件處理上。


function.prototype.apply()


apply()的功能跟call很像,差別在於參數的傳遞方式不同:
call(valueForThis, arg1, arg2, ...) //一個一個指定參數
apply(valueForThis, arrayOfArgs) //將參數放到陣列後傳入
真的說起來,apply()還比call()早出現(前者v1.2,後者v1.5)



2012年11月14日 星期三

[宅] javascript筆記 - window.location類別

window.location是個非常好用的物件,除了可以跳轉外,他還提供了很多讀取所在網址的相關資訊。我們先來看看"http://cat-son.blogspot.tw?foo=bar#a"這個頁面的window.location的attributes:

assign: function () { [native code] } //跳轉
hash: "#a" //取得錨點
host: "cat-son.blogspot.tw"
hostname: "cat-son.blogspot.tw"
href: "http://cat-son.blogspot.tw/#a"
origin: "http://cat-son.blogspot.tw"
pathname: "/"
port: ""
protocol: "http:"
reload: function () { [native code] }
replace: function () { [native code] }
search: "?foo=bar" //網址後的query值>

頁面跳轉

可以用以下幾種方法直接修改window.location的值:
window.location="http://example.com";
window.location.href="http://example.com";
location="http://example.com";
location.href="http://example.com";
或是使用window.location所提供的function來跳轉:
window.location.assign("http://example.com");
window.location.replace("http://example.com");
assign()和replace()效果差不多,差別在於replace是置換掉現在的document,不會留下被置換掉的頁面紀錄,所以當你按下"上一頁"時回到的其實是你"上上個"看到的頁面....
這樣講一定聽不懂,舉個例來說好了:

當我從A頁面逛到B頁面後,在B頁面執行了location.replace(C頁面),會把當前的document從B換成C,所以當我點上一頁時就是回到A。示意:
A-->B
↓
location.replace(C)
↓
A-->C
而使用location.assign則會是這樣的情況:
A-->B
↓
location.assign(C)
↓
A-->B-->C
所以在C頁面點上一頁會回到B而不是A


取得資訊

很多時候我們使用get這種方式來傳遞資料時,傳遞的資料會出現在網址後端,這時我們就可以從window.location.search這個字串拿到query值。例如在"http://example.com/?foo=bar"的頁面下,我們的window.location.search就會是"?foo=bar"
window.location.hash則是achor的值,也就是錨點。
但要注意的是,網址中只要在"#"之後的字串都會被視為hash,所以當你要同時傳遞anchor和query時,務必要把anchor放在最後面,例如:
"http://cat-son.blogspot.tw?foo=bar#a"


重讀頁面

使用window.location.reload(forceGet)
forceGet是個布林值,預設為false,代表只要從cache中重新載入就好,不需要再向server做http request;反之為true時,代表一定要再向server重新要資料。
這個特性還滿重要的,可以避免重讀時拿到舊資料,同樣的目的可以透過加上隨機的query值來達成。


小補充

關於document.location和window.location的差別:
window.location is read/write on all compliant browsers.
document.location is read-only in Internet Explorer (at least), but read/write in Gecko-based browsers (Firefox, SeaMonkey).




2012年11月10日 星期六

[宅] node.js KO Hackthon初體驗

現在是晚上將近11點,小弟的TEAM還在做很廢的功能.....XD
好啦,不說廢話認真作筆記:

MongoDB

之前一直沒有好好鑽研他,直到今天實在是沒法度了(MySQL哭哭),臨時抱佛腳開始使用MongoDB(這個決定好像也是晚上10點決定的Orz)。

下次參加Hackthon要先做功課啊啊啊啊

MongoDB其實是不太需要"建立"所謂的資料庫的,當你需要一個新的資料庫,概念上像是javascript中創建一個object一樣。預設好像是使用test,讓我們來新創一個db object:
> use myDB
switched to db myDB
來看看現在是使用哪個DB呢
> db
myDB
good~然後我們要連到DB時大概像是這樣:
mongodb://usr:password@localhost/myDB

讓我們繼續看下去~(倒)

我又來啦~~ ( ˊㄧˋ)/

在mongo裡面相關權限及重要設定都在admin這個db中,讓我們做以下修改:
 > use admin
switched to db admin
> db.addUser('root','1234');
{ "n" : 0, "connectionId" : 3, "err" : null, "ok" : 1 }
{
        "user" : "root",
        "readOnly" : false,
        "pwd" : "985ddff43226255e09d56b21ad602f69",
        "_id" : ObjectId("509e6d9b3877acdb8eee898c")
}

> db.auth('root','1qaz')
1
之後呢,再以--auth參數重新開啟MongoDB即可

2012年11月7日 星期三

[宅] javascript的模組化設計

最近看到幾篇相當棒的javascript模組化教學文章,在這邊收錄一下:
Javascript模块化编程(一):模块的写法
Javascript模块化编程(二):AMD规范
如何设计大规模 JavaScript 应用 (Part 1 - 概览)
Immediately-Invoked Function Expression (IIFE)

來為所學不精的自己做個學習筆記.....Q__Q


Immediately-Invoked Function Expression(IIFE)

IIFE其實就是像這樣的寫法:
var mod = (function(){var d=0; alert(d);})(); //typeof(mod)為"undifined"
也可以是
var mod = (function(){var d=0; alert(d);}()); //typeof(mod)為"undifined"
上面的寫法跟下面是不一樣的
var mod = (function(){var d=0; alert(d);}); //typeof(mod)為"function"

上面的寫法會直接執行function,但也就只是這樣而已,什麼都沒留下。若只是單純的IIFE其實只要這樣寫就行了:
(function(){var d=0; alert(d);})();



javascript的模組化

但既然要取得一個module我們當然要把內容給keep住,所以得要把需要的attribute包成object後return回來,所以改寫成這樣:
var mod = (function(){
    var d=0;
    var foo=function(){alert(d);};
    return {foo: foo}
})();
這樣我們就可以用mod.foo()來呼叫這個函數,也會alert 0,代表mod.foo()是找得到d的。
但是為什麼不直接向下面這樣定義object就好,還要這麼大費周章呢?
mod={foo: function(){alert(d);}, d:0}
主因就是用這樣的寫的話外部就能看到、也能改動mod.d,而模組化的mod中只找的到mod.foo,從外部是看不到d的。模組化後的物件可以含有像是java的private屬性~所以要讀寫這些屬性就得再寫getter和setter啦。



模組化的規範(CommonJS)

引述 Cat Chen大大的說法:一個模組系統至少要能解決兩個問題:dependencies的載入,以及private、public導出的分別。而CommonJS就是針對server side的javascript的相關規範,其中除了module的規範外還訂定了如Binary strings and buffers、Charset encodings、System process arguments、File system interface、Socket streams、Web server gateway interface、
Local and remote packages and package management等相關規範。更多SPEC可看官方文件:http://commonjs.org/impl/
其實Node.js就是遵照CommonJS的規範,從以下的官方範例就可以看出node.js的影子:

math.js
exports.add = function() {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
        sum += args[i++];
    }
    return sum;
};

increment.js
var add = require('math').add;
exports.increment = function(val) {
    return add(val, 1);
};

program.js
var inc = require('increment').increment;
var a = 1;
inc(a); // 2
module.id == "program";

很明顯的,在CommonJS訂定的module架構中,require到的是module裡所定義的export。



瀏覽器端的模組化規範 - AMD

但是在前面提到的CommonJS規範並不適用於瀏覽器環境,主因是在本機端是從磁碟機載入相關lib,但在client端可是得一個個下載,在同步的情況下必須等到載入完後才會進行接下來的程式,所以頁面看起來就會像是短暫的當機一樣。
這也是為什麼在client端必須以非同步的方式載入(就是同時載入相關module)。但是以非同步的方式載入又會出現一個問題,如果前面的module還沒載入完畢,就被後面的程式所呼叫了呢?
"Asynchronous Module Definition"(AMD)就是為了避免這些問題的出現所訂定的規範。
不同於CommonJS的require,AMD中的require()需要兩個參數:
require([module], callback);
而目前最常見的AMD實作就是require.js了,可以去官網研究一下。



2012年11月5日 星期一

[宅] facebook開發筆記 - javascript SDK(下)

除了前面兩篇()所提到的FB的三個core method,另外也有幾個不介紹不行的好用method:

FB.Event.subscribe & FB.Event.unsubscribe

用來listen相關facebook事件,當特定事件出現時就執行callback function
FB.Event.subscribe(event, callback function)
讓我們來看一段程式碼:
FB.Event.subscribe('auth.sessionChange', function(response) {
     if (response.session) {
        alert(response.authResponse.accessToken);
        //使用者登入,資料寫入cookies裡
     } else {
        alert("Ohoh");
        //使用者登出,cookies被清掉
     }
});

除了範例中所提到的,我們還能訂閱以下的事件:

  • auth.login - fired when the auth status changes to connected
  • auth.logout - fired when your app notices that there is no longer a valid user
  • auth.authResponseChange - fired when the authResponse changes
  • auth.statusChange - fired when the status changes (see FB.getLoginStatus for additional information on what this means)


而傳給callback function的參數"response"長得像這樣:
{
  status: "",         /* Current status of the session */
  authResponse: {          /* Information about the current session */
    userID: ""          /* String representing the current user's ID */
    signedRequest: "",  /* String with the current signedRequest */
    expiresIn: "",      /* UNIX time when the session expires */
    accessToken: "",    /* Access token of the user */
  }
}

而至於unsubscribe的部分則是subscribe的相反,就是取消listen特定事件,可以自己試試看。


FB.login

顧名思義,就是以facebook登入所在網站。前提是你必須先用FB.init設定了SDK環境、設定appId。
FB.login(callback function, permissions scope)
來,讓我們再欣賞一段程式碼範例:
FB.login(function(response) {
   if (response.authResponse) {
     console.log('Welcome!  Fetching your information.... ');
     FB.api('/me', function(response) {
       console.log('Good to see you, ' + response.name + '.');
     });
   } else {
     console.log('User cancelled login or did not fully authorize.');
   }
}, {scope: 'user_about_me,email,user_likes'});
這邊要注意的是第二個參數是一個object,只有一組key:value,key一定是"scope",value是一連串以permission組成的String,中間以逗號分開。
scope中所指定的就是要從使用者那拿到的授權,要看詳細清單可以看facebook的官方文件


FB.getLoginStatus

基本上在一個facebook應用中我們需要判斷使用者是以下三個狀態中的哪一種:
  • 使用者登入了facebook也授權給你的應用程式 (connected)
  • 使用者登入了facebook但還沒授權給你的應用程式 (not_authorized)
  • 根本沒登入 (unknown)

這時我們就可以使用FB.getLoginStatus來搞清楚使用者的狀態
FB.getLoginStatus(callback function, force)
force是一個boolean,會強制reload使用者的登入狀態(預設為false)
我們再來偷一段程式碼來搞清楚他的用法:
FB.getLoginStatus(function(response) {
  if (response.status === 'connected') {
    // the user is logged in and has authenticated your
    // app, and response.authResponse supplies
    // the user's ID, a valid access token, a signed
    // request, and the time the access token 
    // and signed request each expire
    var uid = response.authResponse.userID;
    var accessToken = response.authResponse.accessToken;
  } else if (response.status === 'not_authorized') {
    // the user is logged in to Facebook, 
    // but has not authenticated your app
  } else {
    // the user isn't logged in to Facebook.
  }
 });

response object:
{
    status: 'connected',
    authResponse: {
        accessToken: '...',
        expiresIn:'...',
        signedRequest:'...',
        userID:'...'
    }
}


總結

基本上用facebook javascript SDK開發中常用的、必用的大致上都在這三篇提過了。但是真的要用得很熟練還是要多翻facebook的官方文件。希望這三篇教學筆記能幫助路過的facebook入門開發者。

[宅] facebook開發筆記 - javascript SDK(中)


FB(object)

FB是javascript SDK主要提供服務的物件,在載入函式庫時就會被建立在window底下(FB等於window.FB)。
FB提供很多功能,其中最核心的function有三種:
  • FB.api
  • FB.init
  • FB.ui

前面其實已經使用過FB.init()這個funtion,我們知道它主要是用來初始化SDK環境。為了更了解FB這個object還有些什麼attribute和function,我把它給parse一遍,印出來讓大家能一目了然:
_callbacks: object
api: function
getLoginStatus: function
getAuthResponse: function
getAccessToken: function
getUserID: function
login: function
logout: function
Auth: object
-->getLoginStatus: function
-->fetchLoginStatus: function
-->setAuthResponse: function
-->getAuthResponse: function
-->parseSignedRequest: function
-->xdResponseWrapper: function
Canvas: object
-->isTabIframe: function
-->setSize: function
-->setAutoGrow: function
-->getPageInfo: function
-->scrollTo: function
-->setDoneLoading: function
-->startTimer: function
-->stopTimer: function
-->hideFlashElement: function
-->showFlashElement: function
-->getHash: function
-->setHash: function
-->setUrlHandler: function
-->Prefetcher: object
CanvasInsights: object
-->setDoneLoading: function
share: function
publish: function
addFriend: function
Data: object
-->query: function
-->waitOn: function
-->process: function
Event: object
-->subscribers: function
-->subscribe: function
-->unsubscribe: function
-->monitor: function
-->clear: function
-->fire: function
EventProvider: object
-->subscribers: function
-->subscribe: function
-->unsubscribe: function
-->monitor: function
-->clear: function
-->fire: function
Frictionless: object
-->init: function
-->isAllowed: function
init: function
JSON: object
-->stringify: function
-->parse: function
UA: object
-->nativeApp: function
ui: function
XFBML: object
-->parse: function
-->RecommendationsBar: object

接著我們要介紹幾個重要的function


FB.api

在上一篇提到的API,除了以網址的方式來query資料,我們也能用FB.api()來執行facebook API。(主要是FQL及graph API,login的部分有FB.login()來處理,而REST API已經被捨棄了)
FB.api最多需要四個參數,分別是url、method、提供給API的參數(object)、callback函式:

姓名類型簡介
pathStringthe url path
methodStringthe http method (default "GET")
paramsObjectthe parameters for the query
cbFunctionthe callback function to handle the response



這邊就以FQL來舉個例子,先來看FQL中friends表單的欄位:

可索引姓名類型簡介
*uid1intThe user ID of the first user in a particular friendship link.
*uid2intThe user ID of the second user in a particular friendship link.


今天我們想從FQL中的friends表單中找出使用者的好友,寫成FQL語句就會是這樣:
SELECT uid2 FROM friend WHERE uid1=me()

如果要使用FB.api的話就要寫成這樣:
FB.api(
    "/fql", //path
    {q:"SELECT uid2 FROM friend WHERE uid1=me()"}, //parameter
    function(d) {window.d = d;} //callback function
 );

你可能會發現上面是沒有傳入method的,那是因為如果沒有傳值進去method會預設為GET,所以還是可以正常運作。所以在GET的方法下我們當然也可以寫成:

FB.api(
    "/fql?q=SELECT+uid2+FROM+friend+WHERE+uid1=me()", //path
    function(d) {window.d = d;} //callback function
 );


到了這邊大家可能會發現我一直都沒使用到access_token,這可不是筆誤。使用SDK時,它會自動幫你傳acess_token(我猜的),所以不用特別把它傳過去。但如果你是使用http來query的話就一定要了,所以上面的功能就要寫成這樣:
https://graph.facebook.com/fql?q=SELECT uid2 FROM friend WHERE uid1=me()&access_token=...


FB.ui

主要是用來產生dialog與使用者做互動,例如"發佈到塗鴉牆"、"邀請好友"、"分享連結"、"取得應用程式授權"等等都可以用FB.ui來與使用者溝通,也是個比較禮貌的做法。
那什麼是不禮貌的做法勒?例如說在取得一次使用者的發布塗鴉牆授權後,使用FB.api來直接發布:
FB.api('/me/feed', 'post', { message: body }, function(response){});
也因為FB.ui在動作時都會以彈出視窗讓你知道,所以照理來說應該是不用事先取得這些動作的授權,讓我們來看看要怎樣發布資訊到使用者牆上:
FB.ui(
  {
    method: 'feed',
    name: 'Facebook Dialogs',
    link: 'http://developers.facebook.com/docs/reference/dialogs/',
    picture: 'http://fbrell.com/f8.jpg',
    caption: 'Reference Documentation',
    description: 'Dialogs provide a simple, consistent interface for applications to interface with users.'
  },
  function(response) {
    if (response && response.post_id) {
      alert('Post was published.');
    } else {
      alert('Post was not published.');
    }
  }
);

[宅] facebook開發筆記 - javascript SDK(上)

前面兩篇講到了facebook API,現在我們來講講SDK。

API(Application Programming Interface)直譯成中文是"應用程式介面":只要藉由對方提供的介面丟東西進去就會回傳結果給你。
SDK(Software Development Kit)比較不一樣的是他需要在本機安裝他所提供的函式庫實際跑在本機端,而不是單純使用遠端的服務。

facebook javascript SDK

facebook目前提供JavaScript SDK、PHP SDK、iOS SDK、Android SDK四種開發環境,因為我目前只做web開發,而且也不只用php,所以選擇javascript SDK來做研究、開發。

安裝函式庫&初始化

要使用javascript SDK,必須先include進函式庫,我們來看看以下範例:
<div id="fb-root"></div>
<script>
  window.fbAsyncInit = function() {
    // init the FB JS SDK
    FB.init({
      appId      : 'YOUR_APP_ID', // App ID from the App Dashboard
      channelUrl : '//WWW.YOUR_DOMAIN.COM/channel.html', // Channel File for x-domain communication
      status     : true, // check the login status upon init?
      cookie     : true, // set sessions cookies to allow your server to access the session?
      xfbml      : true  // parse XFBML tags on this page?
    });

    // Additional initialization code such as adding Event Listeners goes here

  };

  // Load the SDK's source Asynchronously
  (function(d, debug){
     var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
     if (d.getElementById(id)) {return;}
     js = d.createElement('script'); js.id = id; js.async = true;
     js.src = "//connect.facebook.net/en_US/all" + (debug ? "/debug" : "") + ".js";
     ref.parentNode.insertBefore(js, ref);
   }(document, /*debug*/ false));
</script>

第一部份(藍字) - "fb-root tag"

只要有facebook javascript SDK的地方就要有fb-root。
但是你會發現即使不加它,程式好像也能正常執行,這是因為在初始化時SDK找不到fb-root,就會幫你新創一個,並在console log中顯示"The "fb-root" div has not been created, auto-creating."
在facebook的官方文件中有提到,若將fb-root設為display: none 或 visibility: hidden,在萬惡IE中可能會不正常運作,但是基本上你也不用考慮它會影響排版,因為初始化時就會把它設為position:absolute且top:-1000之類的,絕對不會讓你看到。所以這部份很單純的嵌入這個小小的div就好。


第二部份(綠字) - 初始化設定

這部份很關鍵,第一部份不用改、第三部份也可以不用改,但是這個地方你至少必須填入你的appId,不然是不能work的,其他參數可以不用改沒關係。
雖然說除了appId以外的參數保留預設值就可以了,但是channelUrl似乎滿值得一提的,所以我在網路上查了一下,在stackoverflow上找到了一個不錯的解釋:

The channel file is to provide a way to do cross domain communication between FB's servers and your own. The reason for this is their tight control over access tokens. You must authenticate a redirect url and app id to retrieve this access token. In order for them to pass you the token, they hit your URL with the access token in the hash. With the Channel URL, they get to pass this token to themselves (their JavaScript running on your domain).
This channel file can then communicate the access token to your active page.

facebook提供給開發者的大部分服務幾乎都得靠access_token來驗證權限,而要取得access_token必須要經過facebook的兩個檢查:redirect urlapp id,他們必須和你在facebook developer那邊所註冊的資料是吻合的(redirect url得在登記的domain下)。也因為這個原因,在沒有預設chanel的狀況下,SDK會強制在你的頁面塞入一個隱藏的iframe以便在你的頁面載入social plugins,這樣一來會造成載入時間長以及效率低落的問題。所以為了提高效能,facebook建議開發者建立http://www.yourdomain.com/channel.html這個檔案,檔案中只要有一行程式碼即可:
<script src="//connect.facebook.net/en_US/all.js"></script>


第三部份(紅字) - 載入facebook javascript SDK

其實只要照範例把這段貼上去即可,不太需要變動它。
這段code主要是檢查document中是否已載入了SDK,如果沒有就嵌入SDK的js檔。在這段code裡面你會看到它所載入的js檔是"//connect.facebook.net/en_US/all.js",其中的en_US是代表洋人美語語系,若要改成台灣用的繁中語系,只要把en_US換成zh_TW就好了。


2012年10月30日 星期二

[宅] javascript筆記(8) - 全域變數&匿名函數

最近在PTT ajax版看到一篇非常不錯的筆記,正好把之前雜亂無章的理解給重新整理了一遍。
原文:http://disp.cc/b/11-4CrK (Knuckles@PTT)

使用全域變數時,由於要往上查找全域,造成效能低落,所以應盡量避免大量使用。
文中作者提到匿名變數的方法:
(function(x){ /*...*/ })(x);
自己寫lib時也曾用這個方法來寫。雖然在匿名函式外看不到裡面所定義的function和變數,但是可以在匿名函式內綁定function和外部觸發事件。也可以將匿名函式裡特定物件設為全域變數,由於scope的關係,外面看的到這個object,這個object也看得到匿名函式裡的其他function和變數。我們以ember.js開頭的一小段程式碼來做了解:
(function() {
  ...
  /**
  @class Ember
  */
  if ('undefined' === typeof Ember) {
    Ember = {};

    if ('undefined' !== typeof window) {
      window.Em = window.Ember = Em = Ember;
    }
  }
  ...
})();
當一個全域變數被定義時,等於是加入到window這個object底下。所以Ember={}跟window.Ember={}是一樣的。
而Ember這個object可能有很多function,例如Ember.deprecate,就可以在全域被看到。但如果Ember.deprecate需要使用一些varible或function,這些東西都必須放在Ember所被定義的匿名函式裡。

從Ember的寫法也能看出來,當include一個lib進來的時候,通常會定義一個特定名稱的Object再將相關的變數及函式都塞在這個Object底下,避免一不小心就重複命名了。

2012年10月26日 星期五

[宅] node.js開發筆記(2) - 以express-vhost來建立虛擬主機 & 背景執行node.js

為了在同一個主機上搭載不同網域的網站,在npm上survey了一下相關套件,最後找到了express-vhost
npm install express-vhost
express-vhost是express的middle-ware,可以透過resiter將特定的網域綁定指定function,來達到以網域做分流的功能。詳情請看:https://github.com/vast-eng/express-vhost

我們希望node能夠在背景執行,而且即使登出主機也能繼續提供服務。
這時我們就要使用forever,他能夠讓你的js檔在背景執行,以下是安裝及啟動方法:
npm install -g forever//記得要設定為全域
sudo forever start server.js//若要以80port開啟服務,記得要以root權限執行

比較要注意的是,利用forever來執行js檔是沒辦法讓你看到console.log的,所以在啟動之前最好用node指令執行一次,確定服務正常再使用forever來開啟sever。尤其是你的server預設listen 80port時,忘了加sudo又沒有相關提示可能會讓你白白debug老半天還找不出原因。

話說其實node最好不要listen 80port,而是用其他伺服器程式來轉接給他,詳細原因可能下一篇再解釋。

2012年10月25日 星期四

[宅] node.js開發筆記(1) - 從安裝到express

最近開始使用node來開發一個vhost 應用~
因為之前是玩的時候是使用windows的環境,這次改在Ubuntu上開發,重新安裝的過程中也順便以筆記整理一下。

<step1> 安裝相關需求
因為這個環境空空如也,非常的乾淨,所以事先必須把一些基本的需求給建立起來
sudo apt-get install python-software-properties
sudo apt-get install make
sudo apt-get install g++
sudo apt-get install git
以上都搞定後就可以從github上下載下來並安裝了:
git clone https://github.com/joyent/node.git
cd node
git checkout v0.6.7
./configure
make
sudo make install
這樣就裝好了~

<step2>安裝npm
npm之於node就好比gem之於ROR,
gem之於ROR就好比apt-get之於Ubuntu....科科

咳,總之npm是node的套件安裝工具,
比較常用到的指令不外乎
npm install -g {你要裝的套件} //-g是代表這個套件可被全域使用,可以不用加
npm search {你要查的關鍵字}
npm list //列出你已安裝的套件

在安裝套件後,npm會把套件的module放在當下資料夾的"node_modules"資料夾中,
在執行node時就可以require該module,前提是執行的js檔所在的資料夾下要有"node_modules"資料夾且裡面那些module才行。
不過照理來說,我安裝的全域套件不用特別塞在裡面就找得到了,事實證明是不行啊~Orz
鳩竟是怎麼回事還得再查查~

<step3>用npm安裝express
express必裝!!!怎麼裝請看下面:
sudo npm install -g express
當要全域安裝時記得加sudo,不然會失敗

<step4>啟動server
我們要使用express的API來設定特定routing的response,將以下程式碼寫入"server.js":
// create server.
    var express = require('express'),
        app = express(),
        port = 1337; 

    app.listen(port);

    // normal style
    app.get('/', function(req, res){
        res.send('hello world');
    });
執行:
node server.js

就可以在localhost:1337上看到"hello world",代表server正常的運行囉~


2012年9月28日 星期五

[宅] javascript筆記(7) - javascriptMVC-Model

最近因為工作的關係開始接觸javascriptMVC,雖然網路上的評價不像Ember那麼的好,也沒有所謂的UI-binding,但是開發團隊很用心的寫了fixture、document等好用的功能,甚至支援command line產出程式碼,一開始用起來的感覺很像是client端的ROR~
至於這些功能有沒有在Ember上被實現我就不知道了,或許有機會可以試試看。

Model:

稍微接觸過MVC架構的朋友應該就知道,Model是負責跟資料庫溝通的部分。
在client端的MVC架構雖然無法直接存取資料庫,但在javascriptMVC中內建了restful的方法,可以經由save(對應update), find(對應select), findAll(select all)等method來呼叫定義的restful:
$.Model('Todo',{
    findAll: 'GET /todos.json',
    findOne: 'GET /todos/{id}.json',
    create:  'POST /todos.json',
    update:  'PUT /todos/{id}.json',
    destroy: 'DELETE /todos/{id}.json'
},{});
這樣就產生了一個Todo的class,而他有著這些restful路徑,我們可以利用Todo來新增一些instance如下:
var todo = new Todo();
這時你可能會想問,那我要怎麼寫入可以被所有instance共同繼承的property呢?
在一開始創建Todo這個class時,我們可以在第三個parameter(型別為Object)內寫入我們要預設的property,就是我標示為紅色的地方啦~

所以假設我在一開始建立Todo這個Model時希望他的instance都能有一個叫做"spotlight"的function以及priority的屬性,我們就要改寫成這樣:

$.Model('Todo',{
    findAll: 'GET /todos.json',
    findOne: 'GET /todos/{id}.json',
    create:  'POST /todos.json',
    update:  'PUT /todos/{id}.json',
    destroy: 'DELETE /todos/{id}.json'
},{
    spotlight : function(){},
    priority : 0
}
);
如此一來被建立出來的todo instance應該會有如下的properties:

priority(number) spotlight(function) constructor(function) Class(function) setup(function) update(function) errors(function) attr(function) bind(function) unbind(function) _updateProperty(function) removeAttr(function) attrs(function) serialize(function) isNew(function) save(function) destroy(function) identity(function) elements(function) hookup(function) created(function) updated(function) destroyed(function) proxy(function) callback(function)

其中save這個function就是對自己這個instance做update的工作,但這時候你可能會發現一但我真的執行了save這個method,傳出的data中卻空空如也,這是因為你不是透過建構子或是.attr( )來對這個instance建立property的!不然總不能要這個instance傻傻地將以上一長串properties全部update上去吧?
為了要讓這個instance知道哪些是自己應該要儲存的attributes,我們可以在建立instance時加上建構子的參數,如下:
var todo = new Todo( {'priority' : 0, 'spotlight' : function(){}} );
或是在創建之後用.attr( )來加入attributes:
todo.attr('priority',0);
這時你可能會想問:「那這個instance是怎麼記錄這些attribute是要被上傳的呢?」
你會發現在這個instance中有一個叫attrs的function,呼叫他之後會返回一個Object,裡面有你到目前為止所增加的attributes,也就是save時所上傳的data!

所以這代表每次每次我們要修改attributes都得透過attr嗎?
答案是不必的~透過建構子或是.attr( )來建立attriutes只是順便做一個類似"註冊"的動作,之後即使是直接進行如下的操作,呼叫attrs後也可以發現數值真的有被修改到:
todo.priorirt=1;
暫時先這樣。

2012年9月24日 星期一

[宅] javascript筆記(6) - jQuery plugin

在使用jqgrid時一定常看到這種用法:
$("#grid").jqgrid(.....);

這種對jQuery元素做操作的method就是利用extend把它綁在上面的,而extend又分兩種用法:
jQuery.extend
jQuery.fn.extend

第一種jQuery.extend可以看作是直接在jQuery上加入一個方法,舉個網路上找到的例子:

 <script type="text/javascript">
        jQuery.extend({
            add: function(a, b) {
                return a + b;
            }
        });
        alert($.add(3, 4)); //7
 </script>
這樣的功能可以直接用"$.定義的function"來執行。

第二種jQuery.fn.extend只能用在jQuery元素上,jqgrid用的就是這種方法了,當要對元素做操作時只要用this即可:

 <script type="text/javascript">
        jQuery.fn.extend({
            red: function() {
                return $(this).css('color', '#ff0000');
            }
        });
        $('#test').red();
</script>

在網路上查了一下也有發現不用extend來做plugin的:

$.fn.hilight = function() {
  // Our plugin implementation code goes here.
};
但這之間有什麼差異就還要再做研究了~

更詳細一點的介紹與範例可以看:ericsk.net-擴充 jQuery


2012年9月11日 星期二

[宅] javascript筆記(5) - 關於z-index、visibility和滑鼠點擊事件


好吧,我承認這篇的主題很亂...


z-index、visibility


首先,z-index的概念很簡單,只要他的值越大代表離你越近,也就是越上層。
visibility則是可以設定成"hidden"或"visible"。

這兩個css屬性是我今天在寫一個light-box的時候所用到的,程式碼如下:

<div id="sb-container" style="visibility: hidden;">
</div>
<div onclick="$('#sb-container').css('visibility','hidden')" style="background-color: black; height: 100%; left: 0px; opacity: 0.8; position: fixed; top: 0px; width: 100%; z-index: 9;">
</div>
<br />
<div id="info-box" style="background-color: white; height: 440px; position: fixed; top: 100px; width: 540px; z-index: 10;">


最外層的"sb-container"是把裡面的兩個div裝起來的這不用多說,裡面的第一個div經由以上的設定後會向一層半透明(opacity:0.8)的遮罩蓋住整個可視範圍。

而在剛創建這個物件時發現有些網頁物件的層級比他稍高,所以我增加了z-index的屬性並設定為比較高的數字(9),這層遮罩就蓋住了整個頁面。

在遮罩之上還要有一個類似dialog的視窗來顯示內容,而他勢必要比遮罩還上面一層,否則會被遮掉,這裡可以看到他的z-index為10。

我想讓這個功能跟facebook顯示圖片的方式一樣,點選黑黑的地方就可以把這個 light  box關掉,所以大家可以看到在遮罩的部分有個$("#sb-container").css("visibility","hidden")的onclick事件。

運用z-index和visibility就可以做出簡單的 light  box效果了。



要補充說明的是,在一般的佈版及配置下,即使visibility為hidden的物件還是會占空間的,若要在隱藏時不要占用空間,可用display:none;


滑鼠點擊事件


當滑鼠點下時,我們可從e中找到一個button屬性,內容可能是0~2其中一個,分別代表是哪個滑鼠按鍵被按下:
0 - 左鍵
1 - 中鍵
2 - 右鍵

2012年9月5日 星期三

[宅] javascript筆記(4) - jQuery selector


以對<table id="t">下所有<td>加掛click事件為例,多半會寫成: 
$("#t td").live("click", fn); 或 $("#t").delegate("td", "click", fn);

學習了學習了~感謝暗黑執行續大大
(http://blog.darkthread.net/post-2011-10-14-delegate-vs-live.aspx)