Gán Nhiều Hàm Xử Lý Sự Kiện Vào Một Phần Tử Trong JavaScript
Chiều nay đang lơ tơ mơ vì vừa ngủ trưa xong (bình thường không ngủ trưa thì thôi chứ cứ ngủ trưa
xong là lơ tơ mơ, chỉ muốn ngủ hết chiều cho sướng :d) thì có bạn hỏi trên group phpvietnam như
thế này: “Em có một input có thuộc tính onclick=”doSomeFunction();” bây giờ muốn thêm
một hàm nữa ví dụ như onclick=“doSomeFunction(); doSomeFunction2();”. Công
việc này có làm bằng Javascript được không ah? Em cám ơn mọi người.“ Nguồn: http://groups.google.com/group/phpvietnam/browse_thread/thread/c7a8688875a320c3
Trả lời bạn ý xong là hết cả buồn ngủ :P, tiện thể tối về viết lại kinh nghiệm cho cái blog đỡ
tủi thân :P. Khi xử lý sự kiện trong JavaScript có 4 mô hình đăng kí sự kiện được phát triển qua
thời gian. Tớ cũng nói thêm về cách sử dụng và xử lý ngữ cảnh (context) với từ khóa this trong các
hàm xử lý. Trong hàm xử lý phải làm sao đạt được 2 mục đích: truyền tham số vào hàm xử lý phải là
event object và từ khóa this trong hàm xử lý sự kiện đó phải là phần tử đã được đăng kí sự kiện. 1. Inline Event Registration Model (Mô hình cổ xưa và quen thuộc nhất)
Từ những ngày đầu khi có JavaScript và xử lý sự kiện thì chỉ có 1 cách duy nhất để gán hàm xử lý sự
kiện cho một phần tử và hiện nay nhiều người vẫn làm theo cách này. Đây là cách cổ xưa và nguyên
thủy nhất:
Mọi người đã quá quen với cách làm như trên, đặc biệt là từ khi viết những dòng code JavaScript đầu
tiên. Viết như vậy không có gì sai cả mà nó còn tiện, nhanh, dễ hiểu và ngày xưa đến nay người ta
vẫn làm thế đấy thôi :P. Tuy nhiên, khi JavaScript phát triển hơn thì cách tiếp cận khác đi chút vì
có nhiều vấn đề nảy sinh như: làm như trên không tách biệt cấu trúc (structure – html) và hành vi
(behavior – JavaScript), làm cho code rối tinh rối mù lên, nhất là khi bạn phải lấy dữ liệu AJAX về
rồi apply template vào thì rất mệt.
Ta có hàm xử lý sự kiện doSomething như sau:
1234
functiondoSomething(evt){alert(arguments[0]);// arguments[0] the same as evtalert(this);}
Với trường hợp này thì evt là undefined và this chính là window object. Giờ làm thế nào để
evt là event object và this là element thì làm theo cách sau:
Như này là đã truyền được event vào event handler rồi nhé. Theo tớ suy luận và thấy thì khi
element được click thì nó sẽ sinh ra sự kiện onclick như sau:
12345
//[Native Code]onclick(event){//event handler được gọi theo cách nàydoSomething(event);}
Nhớ là phải để event là tham số của doSomething(event) chứ ko được dùng tên nào khác. Nếu dùng
tên evt chẳng hạn thì sẽ xảy ra exeption do:
12345
[NativeCode]onclick(event){//Gọi event handler mà có đối số là evtdoSomething(evt);//Xảy ra exception ở đây vì evt là undefined.}
Đây là native code nên nó không cho evt là undefined (null) chứ trong các hàm bình thường thì
truyền tham số là undefined (null) thì không vấn đề gì, ko có exception gì hết :D.
Chú ý: đoạn code trên được test với chrome thì ko thấy lỗi mà vẫn chạy ầm ầm chứ IE, Firefox, Opera
đều xảy ra lỗi hết. Không biết nên chê hay khen bạn chrome đây :d.
Vậy là đến đây lấy được event object rồi, từ event object này lấy được rất nhiều thông tin khác
nhau như tên sự kiện, vị trí con trỏ chuột.v.v… (evt.type; evt.charCode…)
Giờ đến việc xử lý từ khóa this phải là element chứ không phải là window object làm như sau:
onclick(event){//this ở đây là elementdoSomething.call(this,event);}
Trên chính là đoạn code hoàn chỉnh khi muốn lấy this và event object. Tớ nói kỹ ở đây thôi,
mấy đoạn sau sẽ không nói lại mấy cái này nữa. 2. Traditional Event Registration Model
Đây cũng là cách khá nhiều người làm khi bắt đầu muốn tách biệt giữa cấu trúc và hành vi trên trang
web. Trong code JavaScript sẽ đăng kí hàm xử lý sự kiện còn html thì chỉ là html mà thôi.
Đây là cách truyền thống được dùng cũng không phải là ít và hiện nay nó tương thích trên tất cả các
trình duyệt lớn nên ko có gì đáng ngại cả :). Trong mô hình này thì event object sẽ được truyền
luôn vào hàm xử lý sự kiện, còn this trong hàm xử lý sự kiện cũng là element luôn :D.
Có thể làm thế này khi trong hàm doSomething muốn có event object và this là phần tử sinh ra
sự kiện.
Tiếp đến là mô hình thứ 3 và 4 trong xử lý sự kiện. W3C model và Microsoft (IE) model. Sau đó kết
hợp hai mô hình 3 và 4 này được giải pháp khá hoàn chỉnh và có thể sử dụng ngon lành đối với mọi
trình duyệt lớn :). 3. W3C Event Registration Model
Theo mô hình chuẩn của bạn W3C thì đây là cách đăng kí sự kiện cho một element, có thể đăng kí
cùng lúc nhiều hàm xử lý cho 1 element:
eventType ở đây có thể là: click, mouseover, mouseout.v.v… listener chính là hàm xử lý sự kiện được thực thi khi có sự kiện eventType xảy ra. useCapture bạn nên để là false để thống nhất với trình duyệt IE, cái này liên quan tới event
phase mà tớ sẽ nói sau vào một bài viết khác.
Giờ có thể sử dụng như sau:
//javascript, chỉ chạy trên các trình duyệt Firefox, Opera, Chrome// ko chạy được trên IEvarlinkEl=document.getElementById('myLink');linkEl.addEventListener('click',function(){alert(arguments[0]);//event objectalert(this);//linkEl},false);
Rất may là event object lại được truyền và this trỏ luôn đến element nên có thể gọi
doSomething luôn:
Với cách này bạn có thể add bao nhiêu hàm xử lý sự kiện tùy ý, khi có 1 sự kiện xảy ra thì các hàm
xử lý sự kiện đã đăng kí sẽ được thực thi và theo W3C đưa ra thì các hàm này thực thi có thể không
theo thứ tự đăng kí và hiện tại bạn không thể gọi phương thức nào của element để kiểm tra xem sự
kiện nào đó có bao nhiêu hàm xử lý sự kiện đã được đăng kí.
Nhưng muốn bỏ một hàm xử lý đăng kí sự kiện thì làm thế nào? Có luôn:
Cái này rất hiếm khi dùng. Khi muốn remove được một listener nào thì listener đó phải có tên.
Cách add của tớ ở trên kia dùng anonymouse function thì không thể remove được. Muốn remove
được thì phải làm theo cách này:
1234567
varmyListener=function(){alert(arguments[0]);alert(this);//cho chạy một lần đầu rồi remove listener đi luônthis.removeEventListener('click',myListener,false);}linkEl.addEventListener('click',myListener,false);
Đấy là theo W3C, còn Microsoft lại làm theo cách khác. 4. Microsoft Event Registration Model
Bạn IE thì lại chơi kiểu khác, bạn ý không dùng addEventListener mà lại dùng:
1
element.attachEvent(onEventType,handler);
Ví dụ:
123456
varlinkEl=document.getElementById('myLink');//Chỉ chạy trên IElinkEl.attachEvent('onclick',function(){alert(arguments[0]);//event objectalert(this);//linkEl})
Muốn remove một handler (listener) thì dùng detachEvent cũng làm tương tự giống mô hình W3C ở trên:
Chú ý vì IE không có 2 event phase là capture và bubble mà chỉ có bubble phase nên khi sử dụng
với mô hình của W3C thì luôn dùng false với useCapture cho an toàn, nếu hiểu kỹ sự khác biệt của
2 phase này và có mục đích thì mới nên dùng true. 5. Cuối cùng là cách kết hợp mô hình W3C với M$ (IE) thì ta có thể thiết kế và sử api đơn giản như sau:
/** * Cross browser add event listener method. For 'evt' pass a string value with the leading "on" omitted * e.g. addEventListener(window,'load',myFunctionNameWithoutParenthesis,false); * @param obj object to attach event * @param evt event name: click, mouseover, focus, blur... * @param func function name * @param useCapture true or false; if false => use bubbling * @static * @see http://phrogz.net/JS/AttachEvent_js.txt */functionaddEventListener(obj,evt,fnc,useCapture){if(obj===null||evt===null||fnc===null||useCapture===null){alert('all params are required for addEventListener');return;}if(!useCapture)useCapture=false;if(obj.addEventListener){obj.addEventListener(evt,fnc,useCapture);}elseif(obj.attachEvent){obj.attachEvent('on'+evt,function(evt){fnc.call(obj,evt);});}else{myAttachEvent(obj,evt,fnc);obj['on'+evt]=function(){myFireEvent(obj,evt)};}//The following are for browsers like NS4 or IE5Mac which don't support either//attachEvent or addEventListenervarmyAttachEvent=function(obj,evt,fnc){if(!obj.myEvents)obj.myEvents={};if(!obj.myEvents[evt])obj.myEvents[evt]=[];varevts=obj.myEvents[evt];evts[evts.length]=fnc;}varmyFireEvent=function(obj,evt){if(!obj||!obj.myEvents||!obj.myEvents[evt])return;varevts=obj.myEvents[evt];for(vari=0,len=evts.length;i<len;i++)evts[i]();}}/** * removes event listener. * @param obj element * @param evt event name, 'click', 'blur'. 'focus'... * @func function name to be removed if found * @useCapture true or false; false -> bubbling event phase * @static * //TODO make sure method cross-browsered */functionremoveEventListener(obj,evt,func,useCapture){if(obj.removeEventListener){obj.removeEventListener(evt,func,useCapture);}elseif(obj.detachEvent){//IEobj.detachEvent('on'+evt,func);}}
0 nhận xét:
Đăng nhận xét