jQuery განეკუთვნება იმ ბიბლიოთეკათა ნაწილს რომელმაც მოახერხა და გულგრილად არ დატოვა მთელი მსოფლიოს ვებ დეველოპერები. ძალიან ბევრი ამ სფეროში მომუშავე პროფესიონალი თუ მოყვარული დამეთანხმება რომ ამ ბიბლიოთეკის ავტორმა ჭეშმარიტად ეფექტურ და უნიკალურ მიდგომას მიაგნო რამაც უთვალავ ვებ პროგრამისტს არათუ უბრალოდ გაუადვილა არამედ მეტი ხალისი და აზარტი შესძინა მათ ყოველდღიურ საქმიანობას.
ამ პოსტით მინდა გავხსნა დაგეგმილი სერია პოსტებისა რომლის მეშვეობითაც შევეცდები გაგაცნოთ jQuery – ს შიდა სამზარეულო და გადმოგცეთ მასში გამოყენებული ძირითადი მიდგომები რომლებიც დაგეხმარებად მსგავსი ტიპის საკუთარი ბიბლიოთეკების შექმნაში.
პოსტის დანარჩენი ნაწილის წაკითხვამდე მინდა გაგაფრთხილოთ რომ ეს არ არის ტუტორიალი jQuery – ს შესწავლის მსურველთათვის, და იგულისხმება რომ თქვენ უკვე გაქვთ ამ ბიბლიოთეკასთან მუშაობის გარკვეული გამოცდილება.
მაშ ასე შევუდგეთ საქმეს!
პირველ რიგში ვიდრე შევუდგებით კოდის წერასა და ქვედა დონის დეტალების განხილვას, მნიშვნელოვანია გამოვყოთ jQuery – ს ის ძირითადი შემადგენელი კომპონენტები.
- CSS სელექტორ ენჯინი – ეს არის jQuery – ს უმთავრესი შემადგენელი ნაწილი, რომელსაც მექანიკურად ვეხებით ბიბლიოთეკის პირველივე გამოყენებისთანავე. კერძოდ კი ეს არის ძრავი რომელიც DOM დოკუმენტში CSS 3 – თან თავსებადი სელექტორების მეშვეობით ელემენტების მოძებნის საშუალებას გვაძლევს;
- ატრიბუტები – სპეციალური მეთოდები ელემენტების ატრიბუტებთან სამუშაოდ;
- ტრავერსინგი – სპეციალური მეთოდები უკვე ნაპოვნი DOM ელემენტების დამატებითი ფილტრაციისა და ნავიგაციისთვის;
- ელემენტებით მანიპულაცია – სპეციალური მეთოდები ელემენტებზე სხვადასხვა DOM ოპერაციების ჩასატარებლად(მაგ. წაშლა, ჩამატება, გადატანა, კლონირება);
- CSS მანიპულაცია – DOM ელემენტების CSS სტილებთან მუშაობა;
- ივენთები – DOM ელემენტების ივენთების მენეჯმენტი;
- Ajax – XMLHttpRequest ობიექტის გარშემო აგებული სპეციალური მეთოდები სერვერთან ასინქრონულ რეჟიმში მუშაობისათვის;
ეს არის ნაწილი იმ ძირითადი კომპონტენტებისა რომლისგანაც შედგება jQuery. აღსანიშნავია რომ ყოველი კომპონენტი საკმაოდ კომპლექსურია და ყველა მათგანს გააჩნია მათთვის დამახასიათებელი სირთულეები რაც ძირითადად ბრაუზერებს შორის თავსებადობაში გამოიხატება.
ამ მცირედი შესავლის შემდეგ შეგვიძლია გადავიდეთ საქმეზე და შევუდგედ კოდირებას, თუმცა, მანამდე სანამ დავწერთ კოდს უნდა განვიხილოთ jQuery – ს ორ მაგიურ დეტალს რომელიც თავის თავში აერთიანებს ყველა ზემოთ ჩამოთვლილ კომპონენტს და განსაკუთრებულ ხიბლსა და ელეგანტურობას სძენს მიმდინარე პოსტის გმირს. პირველია ფუნქცია რომლის სახელიც არის დოლარის აღმნიშნველი სიმბოლო:
1 2 3 | //ცნობილი დოლარის ნიშანი რომელიც რეალურად //არის მიმთითებელი გლობალურ jQuery ობიექტზე $('#dom-element-id'); |
ხოლო მეორე ე.წ. chaining რომლის მეშვეობითაც ჯაჭვურად შეგვიძლია გამოვიძახოთ სხვადასხვა ტიპის მეთოდები და განვახორციელოთ სხვადასხვა მაინპულაციები CSS მოთხოვნის მიხედვით ნაპოვნ DOM ელემენტების კოლექციაზე:
1 2 3 4 5 | $('#dom-element-id') //ვიპოვნოთ ელემენტი იდენტიფიკატორით .find('li') //ნაპოვნ ელემენტში ვიპოვნოთ ყველა LI ელემენტი .addClass('found') //ნაპოვნ LI ელემენტებს მივანიჭოთ CSS კლასი .find('span') //LI ელემენტებში ვიპოვოთ ყველა SPAN ელემენტი .remove(); //გავანადგუროთ ნაპოვნი SPAN ელემენტები |
კოდის ეს ორი მცირე ფრაგმენტი ძალიან მარტივი და ელეგანტურია მითუფრო თუ გავითვალისწინებთ მთელს იმ სირუთელეებს რაც ამ რამდენიმე მეთოდის გამოძახების უკან დგას! მეთოდების ასე ჯაჭვურად გამოძახების შესაძლებლობა
კიდევ ერთი საინტერესო და jQuery – სთვის დამახასიათებელია დეტალი
გიფიქრიათ ოდესმე მსგავსი რამის საკუთარი ხელით შექმნაზე? არა? ან გიფიქრიათ და დაგზარებიათ? თუ მსგავსი რამ ჯერ არ გაგიკეთებიათ ესე იგი უკვე დროა დავიკაპიწოთ ხელები და შევუდგეთ საკუთარი jQuery – ს მსგავსი ბიბლიოთეკის შექმნას. ამ პოსტში და მის შემდგომ ნაწილებში ამ ბიბლიოთეკას პირობითად დავარქვათ myQuery და შევუდგეთ იმპლემენტაციას.
პირველ რიგში გვესაჭიროება “$” ფუნქცია რომელიც წვდომას მოგვცემს myQuery კლასის ობიექტზე. ფუნქცია ძალიან მარტივია, მას გადაეცემა ორი პარამეტრი – CSS სელექტორი და კონტექსტი(არააუცილებელი პარამეტრი). აღწერილი მიზნის მისაღწევად სრულებით საკმარისია ქვემოთ ნაჩვენები კოდი:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | (function() { /** * @class myQuery class * * @param query String|DOM Element * @param context DOM Element|DOM Element Array */ var myQuery = function(query, context) { }; /** * myQuery prototype */ myQuery.prototype = { }; /** * Add "$" function to global context * function accepts same arguments as myQuery class * * @param query String * @param context DOM Element|DOM Element Array */ window.$ = function(query, context) { //return new "myQuery" instance return new myQuery(query, context); }; })(); |
პირველ რიგში ყურადღებას შევაჩერებ კოდის რამდენიმე დეტალზე. კერძოდ კი ყუარდღებას იმსახურებს შემდეგი კონსტრუქცია:
1 2 3 | (function() { })(); |
ეს ერთი შეხედვით უცნაური კოდი ექვივალენტურია შემდეგი კოდისა:
1 2 3 4 5 6 7 | //ფუნქციის აღწერა function myFunction() { //some initialization code here } //ფუნქციის გამოძახება myFunction(); |
თუმცა ჩვენს შემთხვევაში ტრადიციული გზა მთლიანად ავიცილეთ თავიდან რადგან აბსოლუტურად არაფერში არ გვესაჭიროება დამოუკიდებელი ფუნქციის აღწერა ინიციალიზაციისათვის რადგან ეს ფუნქცია უნდა შესრულდეს მხოლოდ ერთხელ კოდის ჩატვირთვისთანავე და კოდის სხვა ნაწილიდან მისი გამოძახების არანაირი აუცილებლობა არ არსებობს. გარდა ამისა ფუნქციის ავტომატურ გამოძახებასთან ერთად ჩვენი კოდის დანარჩენი ნაწილი მოქცეულია ერთ კონკრეტულ კონტექსტში(კონტექსტი შემოიფარგლება ანონიმური ფუნქციის შიდა კონტექსტით) და არ ხდება გლობალური(window) კონტექსტის დაბინძურება. კოდის დანარჩენი ნაწილი ჩვეულებრივი ფუნქციის აღწერებია და განსაკუთრებულ ახსნა განმარტებას არ იმსახურებს, თუმცა ყოველი შემთხვევისთვის აღვნიშნავ რომ შემდეგი კოდის მეშვეობით:
1 | window.$ = function(query, context) |
გარდა იმისა რომ “$” ფუნქციას ხელმისაწვდომს ვხდით გლობალურ(window) კონტექსტში, ამ ფუნქციას უნარჩუნდება წვდომა ზემოთ ნახსენები ანონიმური ფუნქციის კონტექსტზე ისე რომ ამ კონტექსტს მიღმა არსებულ სხვა კოდს მასთან არანაირი შეხება არ გააჩნია.
მოცემული კოდის მეშვეობით უკვე შეგვიძლია შევასრულოთ შემდეგი ტიპის გამოძახებები:
1 2 | $('div.my-div-class-name a.selected'); $('div.my-div-class-name', document.getElementById('any-element-id')); |
თუმცა ცხადია ნაჩვენები კოდი არანაირ რეზულტატს არ მოგვცემს რადგან myQuery ჯერჯერობით არანაირი ფუნქციონალობით არ არის აღჭურვილი. იმისათვის რომ ჩვენმა რეალიზაციამ გააკეთოს რაიმე სასარგებლო პირველ რიგში საჭიროა იგი აღვჭურვოთ ნახსენები CSS სელექტორების ძრავით. საწყის ეტაპზე ამ მიზნის მისაღწევად საკმარისი იქნება თუ ვისარგებლებთ ბოლო ვერსიების(Safari, Chrome, Firefox 3.5, Opera 10, InternetExplorer 8 ) ბრაუზერებში ჩადებული “querySelectorAll” მეთოდით რომლის მეშვეობითაც შესაძლებელი იქნება დოკუმენტისა ან ნებისმიერი DOM ელემენტის კონეტქსტში მოვძებნოთ სხვა DOM ელემენტები CSS სელექტორის გამოყენებით. ამ მეთოდის გამოსაყენებლად საჭიროა myQuery შევცვალოთ შემდეგნაირად:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | var myQuery = function(query, context) { //if no context provided use "document" as default context = context || document; //if context is DOM document or Node put it in array if (!(context instanceof Array)) { context = [context]; } //resulting array of found DOM elements var result = []; try { //loop through each context and find elements for (var i = 0, length = context.length; i < length; i++) { //find elements within context var nodes = context[i].querySelectorAll(query); //build array from DOM List object for (var j = 0, len = nodes.length; j < len;) { result.push(nodes[j++]); } } } catch(ex) { result = []; } //count of found DOM elements //will be updated with Array's push() method this.length = 0; //add all found elements to "myQuery" instance Array.prototype.push.apply( this, //current instance of myClass object result //array of found DOM elements ); } |
მოდიფიცირებული ფუნქცია საკმაოდ მარტივია. პირველ რიგში იგი querySelectorAll მეთოდისა და ფუქციისათვის პირველ პარამეტრად გადაცემული CSS სელექტორის მეშვეობით კონკრეტულ კონტექსტში ეძებს დომ ელემენტებს. როგორ მუშაობს ეს ფუნქცია?
- მე – 4 სტრიქონი – თუ გამოძახება მოხდა კონტექსტის პარამეტრის გარეშე ნაგულისხმევ კონტექსტად გამოიყენება “document” ობიექტი;
- 7/9 სტრიქონები – ამ შემთხვევაში საჭიროა შევამოწმოთ არის თუ არა კონტექსტი მასივის ტიპის რადგან მსგავსი რამ დასაშვებია. თუ კონტექსტი არ არის მასივი და DOM დოკუმენტი ან DOM ობიექტი ჩავდოთ იგი მასივში და ხელახლა მივანიჭოთ იგი “context” ცვლადს;
- 17/27 სტრიქონები – ციკლში ყოველი კონტექსტისათვის შევასრულოთ “querySelectorAll” მეთოდი.
- 35 – ე სტრიქონი – აღვწეროთ ობიექტის “length” თვისება ;
38/40 სტრიქონები – ნაპოვნი DOM ელემენტების დამატება ხდება თავად “myQuery” ობიექტში რაც ნიშნავს იმას რომ მაგ მომენტიდან myQuery ობიექტი შეგვიძლია გამოვიყენოთ როგორც მასივი. მაგალითად ქვემოთ ნაჩვენები ყველა მაგალითი აბსოლუტურად ვალიდურია:
1 2 3 4 5 6 7 8 9 | alert($('div')[0]); //ან var result = $('div'); alert(result[0]); //ან var result = $('div'); for (var i = 0; i < result.length; i++) { alert(result[i].nodeName); } |
ობიექტის აღჭურვა მასივის მსგავსი ქმედებებით ამ კონკრეტულ შემთხვევაში მიიღწევა Array ობიექტის push მეთოდისა და myQuery ობიექტის “length” თვისების მეშვეობით. ამონარიდი JavaScript – ის დოკუმენტაციიდან:
push is intentionally generic. This method can be called or applied to objects resembling arrays. The push method relies on a length property to determine where to start inserting the given values. If the length property cannot be converted into a number, the index used is 0. This includes the possibility of length being nonexistent, in which case length will also be created
თუ კოდის ეს ნაწილი მაინც გაუგებარია თქვენთვის გირჩევთ გადაიკითხოთ ფუნქციის call და apply მეთოდების შესახებ ჩემს მიერ რამდენიმე წლის წინ დაწერილი პოსტი.
იმედია ამ მომენტამდე ყველაფერი გასაგებია
დროა გადავიდეთ შემდეგ ეტაპზე და ჩვენი myClass ბიბლიოთეკა აღვჭურვოთ რამდენიმე სასარგებლო მეთოდით. პირველი ეტაპისათვის საკმარისი იქნება შემდეგი რამდენიმე მეთოდის იმპლემენტაცია:
- each – მეთოდი ნაპოვნი ელემენტების იტერაციისათვის, ქოლბექ ფუნქციის პარამეტრად გადაცემის შესაძლებლობით
1 2 3 4 5 6 7 8 9 10 | myQuery.prototype = { each: function(callback) { for (var i = 0, el; (el = this[i++]);) { callback.call(el, i); } return this; } }; |
1 2 3 | $('div p').each(function(index) { this.innerHTML = "<b>This is " + index + "th paragraph</p>"; }); |
each ფუნქციის რეალიზაცია ძალიან მარტივია, “$” შესრულების შემდეგ “myQuery” ობიექტი უკვე შეიცავს ყველა ნაპოვნ DOM ელემენტს. გამომდინარე აქედან ელემენტებს შეგვილია მივმართოთ ინდექსით მაგ. “this[0]“, ხოლო რადგან myQuery ობიექტს გააჩნია length თვისება მისი იტერაცია და სათითაო ელემენტის ამოღება შესაძლებელია ჩვეულებრივი ციკლის ოპერატორით. ციკლის შიგნით უბრალოდ ხდება each მეთოდისათვის პარამეტრად გადაცემული ქოლბექ ფუნქციის გამოძახება call მეთოდის მეშვეობით მსგავსი გამოძახება გარანტიას გვაძლევს რომ ქოლბექ ფუნქცია შესრულებული იქნება პირველ პარამეტრად(კონტექსტი) გადაცემული DOM ელემენტის კონტექსტში. ხოლო ქოლბექფუნქციის შიგნით მიმდინარე DOM ელემენტს პირდაპირ შეგვიძლია მივმართოთ this – ის მეშვეობით.
ასევე each ფუნქციის რეალიზაცია განსაკუთრებულ ყურადღებას იმსახურებს ფუნქციის ბოლო სტრიქონი:
1 | return this; |
რეალურად myQuery ობიექტის each მეთოდი აბრუნებას ისევ myQuery ობიექტს ხოლო ეს ნიშნავს რომ ე.წ. Chaining უკვე უზრუნველყოფილია და წინა მაგალითის მოდიფიცირებით უკვე შეგვიძლია დავწეროთ შემდეგი კოდი:
1 2 3 4 5 | $('div p').each(function(index) { this.innerHTML = "<b>This is " + index + "th paragraph</p>"; }).each(function(index) { this.id = 'paragraph' + index; }); |
ყურადღება მიაქციეთ each – ის ჯაჭვისებურად გამოძახებას.
კოდის დასრულებული ვერსია:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | (function() { /** * @class myQuery class * @param query String|DOM Element * @param context DOM Element|DOM Element Collection */ var myQuery = function(query, context) { //if no context provided use "document" as default context = context || document; //if context is DOM document or Node put it in array if (!(context instanceof Array)) { context = [context]; } //resulting array of found DOM elements var result = []; try { //loop through each context and find elements for (var i = 0, length = context.length; i < length; i++) { //find elements within context var nodes = context[i].querySelectorAll(query); //build array from DOM List object for (var j = 0, len = nodes.length; j < len;) { result.push(nodes[j++]); } } } catch(ex) { result = []; } //count of found DOM elements //will be updated with Array's push() method this.length = 0; //add all found elements to "myQuery" instance Array.prototype.push.apply( this, //current instance of myClass object result //array of found DOM elements ); }; //myQuery class methods myQuery.prototype = { each: function(callback) { for (var i = 0, el; (el = this[i++]);) { callback.call(el, i); } return this; } }; /** * Add "$" function to global context * function accepts same arguments as myQuery class * * @param query String|DOM Element * @param context DOM Element|DOM Element Collection */ window.$ = function(query, context) { //return new "myQuery" instance return new myQuery(query, context); }; })(); |
ტეგები: chaining, css selector, jquery, myquery
ჩასაყლაპი პოსტია პირდაპირ
)
@Samurai
დიდი მადლობა, საოცარი კომპლიმენტია პოსტმა დაიმორცხვა
მაგარია, ველოდებით გაგრძელებას
ბიჭოს, რამდენი უწერია. მოხვედი ჯანებზე?
მაგარი პოსტია, საღოლ.
@ირაკლი
გაიხარე
კი მგონი მოვდივარ ისევ ჯანზე
@ლეკვა
გაგრძელება მალე იქნება
rame isetic dawere mec ro momewonos ra
:D:D
ara ara vxumrob kaia egre gaagrdzele