პოსტები ტეგით “JavaScript DOM”

Handling “onchange” event revisited

რამდენიმე თვის წინ დავწერე პოსტი, HTML ფორმის ელემენტის onchange ივენთისა და მისი დელეგირების შესახებ.

ნახსენებ პოსტში ნაჩვენებია onchange ივენთის დელეგირების მაგალითი სადაც ფორმის ყველა ელემენტის აღნიშნული ივენთის ცენტრალიზებული კონტროლი ხორციელდება მშობელი ელემენტის onchange ივენთის მეშვეობით.

მსგავსი შედეგის მიღწევა შესაძლებელი ე.წ. Event Bubbling – ის წყალობით რაც ივენთის, საწყისი ელემენტიდან მშობელი ელემენტისკენ გავრცელებას ეწოდება. მეტი თვალსაჩინოებისთვის იხ. სურათი(სურათისათვის განსაკუთრებული მადლობა ირაკლის):

picture-17

ივენთის გავცელების(შვილიდან მშობლის მიმართულებით) ასეთი მეთოდი ყველა ბრაუზერში ერთნაირად მუშაობს. და წესით ეს უნდა ეხებოდეს ყველა ტიპის ივენთს. თუმცა როგორც მოსალოდნელი იყო IE – ში მისი მერვე ვერსიის ჩათვლით სწორედ ამ ივენთზე აღმოჩნდა შეზღუდვა და იგი საწყისი ელემენტს(ელემენტი რომელზეც უშუალოდ წარმოიშვა ეს ივენთი) იქეთ არ ვრცელდება.

ერთი შეხედვით, ამ ჩიხიდან რამდენიმე გამოსავალი შეიძლება მოიძებნოს:

  1. ფორმის ყველა ელემენტის ჩამოვლა და მათთვის onchange ივენთ ჰენდლერის მინიჭება. თუმცა აქ მთელ რიგ პრობლემებს წავაწყდებით:

    • ელემენტებზე ასეთი გზით ივენთ ჰენდლერების მინიჭება IE – ში იწვევს მეხსიერებასთან დაკავშირებულ პრობლემებს, ე.წ. memory leaks. ეს გარემოება განსაკუთრებით მნიშვნელოვანია თუ ფორმაში ბევრი ელემენტები გვაქვს.
    • თუ ფორმის ელემენტები იტვირთება დინამიურად Ajax – ის მეშვეობით, ყოველი ასეთი ჩატვირთვის შემდეგ საჭიროა: ა) ყველა ფორმის ელემენტისა და მათი ივენთ ჰენდლერების განადგურება რათა თავიდან ავირიდოთ მეხსიერებასთან დაკავშირებული პრობლემები; ბ) ახლად ჩატვირთული ელემენტების იტერაცია და მათთვის შესაბამისი ივენთ ჰენდლერების განსაზღვრა.

    დამეთანხმებით რომ აღწერილი ორი მეთოდი დიდი მოქნილებით არ გამოირჩევა.

  2. ნაცვლად onchange ივენთისა გამოვიყენოთ onfocus და onblur ივენთები. თუმცა აღმოჩნდა რომ არც ამ ივენთების გავრცელება არ ხდება ტრადიციული(შვილიდან მშობლის მიმართულებით) მეთოდით.

გარკვეული კვლევა ძიების შემდეგ მივაგენი ისეთ ივენთებს(დიდი მადლობა PPK – ს) რომელთა ბაბლინგი IE – ში ისე მუშაობს როგორც საჭიროა. ეს ივენთებია onfocusin და onfocusout.

როგორ შეიძლება აღნიშნული ივენთების გამოყენება პრობლემის გადასაჭრელად? სანამ უშუალოდ კოდის წერას დავიწყებთ საჭიროა კარგად გავერკვეთ თუ როგორ მუშაობს onchange ივენთი HTML ფორმის ცალკეული ელემენტებისათვის და ასევე თუ რა გვერდის ავლის მექანიზმების გამოყენებაა შესაძლებელი IE – ში.

SELECT ელემენტი

ამ ელემენტის შემთხვევაში onchange ივენთი წარმოიშვება მხოლოდ იმ ჩამოსაშლელი სიიდან კონკრეტული ელემენტის ამორჩევის დროს. IE – ში იგივე ხდომილების ემულაციისათვის შესაძლებელია onclick ივენთის გამოყენება, თუმცა, ასეთ შემთხვევაში ივენთი ორჯერ წარმოიშვება ერთხელ ელემენტის გააქტიურებისას ხოლო მეორედ ჩამოსაშლელი სიიდან მნიშვნელობის არჩევისას. თუმცა ამ პრობლემის არიდება მარტივად არის შესაძლებელი სწორედ onfocusin და onfocusout ივენთების მეშვეობით რადგან ეს ორივე ივენთი წარმოიშვება onclick ივენთამდე, გამომდინარე აქედან შესაძლებელია წინა ივენთის ტიპის დამახსოვრება და შემდგომ უკვე დადგენა ეს იყო პირველი კლიკი თუ ჩამოსაშლელი მენიუდან ელემენტის არჩევა მოხდა?

INPUT text და TEXTAREA ელემენტები
ამ ელემენტების შემთხვევაში onchange ივენთი წარმოიშვება როდესაც ელემენტი კარგავს ფოკუსს. IE – ში იგივეს მიღწევა შესაძლებელია onfocusout ივენთის მეშვეობით.

INPUT checkbox და radio ელმენტები
ამ ელემენტების შემთხვევაში onchange ივენთი წარმოიშვება მათი მნიშვნელობის ცვლილების თანავე. IE – ში იგივე შედეგის მისაღებად მარტივად არის შესაძლებელი onclick ივენთის გამოყენება.

მაშ ასე წინა სამი პარაგრაფში გამოიკვეთა სამი ისეთი ივენთი რომელთა გამოყენებაც შესაძლებელია onchange ბაბლინგის ემულაციისათვის. ეს ივენთებია: onclick, onfocusin, onfocusout.

ამ ინფორმაციაზე დაყრდნობით მარტივად არის შესაძლებელი სულ რამდენიმე სტრიქონი კოდით გადავწყვიტოთ პრობლემა:

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
(function() {
   
   var div = document.getElementById('testonchange');
   
   //ყველა ბრაუზერი IE - ს გარდა
   if (document.addEventListener) {

      div.onchange = function(e) {
         //აქ შეგვიძლია გამოვიძახოთ ივენთის უშუალო ჰენდლერი
      };

   } else { //IE - ს სპეციფიური რეალიზაცია
     
      var previousEvent = null;
     
      div.onfocusin = div.onfocusout = div.onclick = function() {

         var e            = window.event,
            target       = e.srcElement,
            isSelect     = target.nodeName  == 'SELECT',
            notTextInput = target.nodeName  == 'INPUT' && /^radio|checkbox$/i.test(target.type),
            isTextInput  = (target.nodeName == 'INPUT' && target.type == 'text') || target.nodeName == 'TEXTAREA',
            validElement = notTextInput || isSelect;

         if (isSelect && !target.size && /^focus(in|out)$/.test(previousEvent)) {
            validElement = null;
         }

         previousEvent = e.type;

         if ((e.type == 'click' && validElement) || (e.type == 'focusout' && isTextInput)) {
            //აქ შეგვიძლია გამოვიძახოთ ივენთის უშუალო ჰენდლერი
         }

      };

   }
   
   div = null;
   
})();

ეს არის და ეს :) კოდის დეტალური გარჩევით თავს აღარ შეგაწყენთ, ხოლო რეალური დემონსტრაცია შეგიძლიათ იხილოთ აქ.

ტეგები: , ,

HTML Form “elements” Property

ამ პოსტის დაწერამდე რამდენიმე წუთით ადრე წავაწყდი HTML form ელემენტის elements თვისებასთან დაკავშირებულ ერთ პრობლემას(შეცდომას) რომლის შესახებაც აქამდე არაფერი მსმენია და ცოტა არ იყოს უაზროდ დამახარჯინა დრო.

ცნობილია, რომ form ელემენტის აღნიშნული თვისება არის HTMLCollection ტიპის კოლექცია და იგი შეიცავს კონკრეტული HTML ფორმის შიგნით გამოყენებულ ყველა(input, select, button) ელემენტს. სწორედ ეს კოლექცია წარმოადგენს საჭირო ფორმის ელემენტებთან წვდომის ყველაზე უსაფრთხო(თურმე პირობითად) გზას.

როგორც აღმოჩნდა თუ ფორმის ელემენტებს მოვათავსებთ fieldset ტეგში(და წესით ეს ასეც უნდა გავაკეთოთ) elements კოლექცია მოულოდნელ რეზულტატს დაგვიბრუნებს. კერძოდ კი fieldset ელემენტიც(ან ელემენტები) ამ კოლექციაში აღმოჩნდება.

ჩემი ამოცანა მდგომარეობდა შემდეგში, რომ ფორმის ყველა ელემენტებისაგან მიმეღო მათი სახელებისა და მნიშნვნელობების ე.წ. key/value ობიექტი:

1
2
3
4
5
var map = {}, els = target.form.elements;
for (var i = 0, el; (el = els[i++]);) {
   map[el.name] = el.value;
}
console.log(map);

თუმცა ძალიან უცნაური შედეგი მივიღე რადგან fieldset ელემენტს არც name და არც value თვისებები არ გააჩნია.

პრობლემის იდენტიფიცირების შემდგომ ინტერნეტში მოვიძიე მცირეოდენი ინფორმაცია:

The HTML 4 standard adds new <fieldset> and <label> tags to the set of elements that can appear within a form. In IE 5 and later, placing a <fieldset> in a form causes a corresponding object to be added to the form’s elements[] array. Fieldset elements are not scriptable in interesting ways like other form elements are, and their objects do not have a type property like other form elements do. Therefore, the presence of Fieldset objects in the elements[] array seems like a mistaken design decision. This is particularly true since <label> tags do not cause corresponding objects to be added to the elements[] array. The Mozilla and Netscape 6 browsers have chosen to follow Microsoft’s lead on this in order to be compatible with IE.

What this means is that if you define a form that contains fieldsets, the contents of the elements[] array differ in recent, HTML 4-capable browsers and in older, pre-HTML 4 browsers. In this situation, using position-based numeric indexes in the elements[] array is not portable, and you should define name attributes for all your form elements and refer to them by name.

მოკლედ კვლავ IE :D ნუ რას ვიზამთ რეალობა ასეთია :P

ტეგები: , ,

jCSS – CSS Selector Engine

Hi all, ესე იგი მინდა წარმოგიდგინოთ ჩემი ერთერთი პატარა პროექტი სახელად jCSS. იგი წარმოადგენს JavaScript – ზე დაწერილ ე.წ. CSS Selector Engine – ს რომლის მეშვეობითაც მარტივად შეძლებთ HTML დოკუმენტის DOM ელემენტების ძებნას და რეზულტატის მიღებას ტიპიური CSS სელექტორების გამოყენებით.

მინდა აღვნიშნო რომ არავისთვის კონკურენციის გაწევას არ ვაპირებ ანუ jQuery, Mootools, Seezle და ა.შ. :D ეს არის ჩემი პირადი ექსპერიმენტი რომელსაც გარდა პრაქტიკული დანიშნულებისა გააჩნია წმინდა შემეცნებითი დატვირთვა და ამ მინი პროექტზე მუშაობისას ჩემს ძირითად ამოცანას წარმოადგენდა DOM ელემენტებში ნავიგაციისა და ციკლების წარმადობის კვლევა. ნებისმიერ შემთხვევაში სკრიპტი სრულად ფუნქციონალურია და შეგიძლიათ გამოიყენოთ.

წარმადობისა და რეალიზებული CSS სელექტორების ტესტი შეგიძლიათ ნახოთ აქვე. ტესტირებისათვის ვიყენებ Mootools – ის ავტორის მიერ შექმნილ ტესტირების სისტემას სახელად SlickSpeed ასე რომ შედეგებში ტენდენციურობა გამორიცხული :D შედარებისათვის გარდა jCSS – ისა ტესტში გამოვიყენე jQuery – ისა და Mootools – ის ბოლო სტაბილური ვერსიები.

გადაწერეთ jCSS!

გამოყენების მაგალითები:

1
2
3
4
var els = jCSS('#my-element-id div.my-class-name ul a');
for (var i = 0; i < els.length; i++) {
   //do something wit elements
}

სულ ეს არის და ეს, ამ ეტაპზე მეტის დაწერას აზრი არ აქვს. თუ თქვენს მოსაზრებებსაც გამიზიარებთ ძალიან ძალიან გამახარებთ.

enjoy :)

Update:
განვაახლე jCSS აწი უფრო სწრაფია FF, Opera და IE – ში.

SlickSpeed Benchmark: http://www.code.ge/jcss-test/slickspeed/

დავამატე Unit Test Suite ბრაუზერებს შორის და სხვადასხვა ტიპის სელექტორების იმპლემენტაციის სისწორის კონტროლისათვის:
Unit Test Suite: http://www.code.ge/jcss-test/slickspeed/frameworks/test/

Update:

  • დაიფიქსა ყველა ძირითადი ბაგი;
  • დაიხვეწა და განვითარდა Query Parser;
  • დაემატა ყველა ე.წ. ფსევდო კლასების მხარდაჭერა გარდა nth-child და nth-last-child ფსევდო კლასებისა;
  • კომპლექსური ფსევდო კლასების მხარდაჭერა;
  • დავამატე DOM ელემენტების სორტირება ნეითივ სელექტორების მიერ დაბრუნებულ რეზულტატებთან თავსებადობის გამო. თუმცა ამან წარმადობა შედარებით დაწია IE – სა და Opera – ში.
  • დაიხვეწა კონტექსტური პარამეტრების მხარდაჭერა, გარდა DOM ელემენტისა კონტექსტურ პარამეტრად შესაძლებელია გამოვიყენოთ როგორც სელექტორი ასევე DOM ელემენტების მასივი. მაგ:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
      var nodes = jCSS(
       'div > p.paragraph-class-name',
       document.getElementById('my-element-id')
     );

     var nodes = jCSS(
       'div > p.paragraph-class-name',
       '#my-element-id:visible'
     );

     var nodes = jCSS(
      'a em',
      document.getElementsByTagName('h1')
     );
  • განვაახლე SlicckSpeed ანუ დავამატე YUI!, Prototype და Peppy სელექტორ ენჯინები;

    SlickSpeed Benchmark: http://www.code.ge/jcss-test/slickspeed/

  • განვაახლე და გავამდიდრე UnitTest ფრეიმვორკი:

    Unit Test Suite: http://www.code.ge/jcss-test/slickspeed/frameworks/test/

კომპლექსური სელექტორების იმპლემენტაცია საშუალებას იძლევა ელემენტები მოვძებნოთ დაახლოვებით მსგავსი სელექტორების გამოყენებით:

1
2
3
4
5
#form option:not(:contains('Nothing'),#option1b,:selected)

#form option:not(:not(:selected))[id^='option3']

:input:not(:image,:input,:submit)

ამ ეტაპზე ნეითივ სელექტორები გათიშული მაქვს Safari, Chrome, IE8 და FF3.1 – სთვის უკუთავსებადობის სრული ტესტირებისათვის. გარდა მაგისა არც ნეიტივ სელექტორები არ არის სრულყოფილად რეალიზებული და ნებისმიერ შემთხვევაში საჭიროა ე.წ. Custom Implmentation(განსაკუთრებით ეს ეხება IE8 – ს).

ტეგები: , , ,

JavaScript WTF Vol. 5 – magic of onchange event

ხშირად იყენებთ HTML ფორმის ელემენტების ამ ივენთს? :) და რა შემთხვევაში? ალბათ უფრო SELECT ელემენტთან მუშაობისას ხომ? თქვენი არ ვიცი მაგრამ ჩემს პირად პრაქტიკაში განსაკუთრებული გამოყენება ამ ივენთს არ ჰქონია… თუმცა ორიოდე დღის წინ ყველაფერი შეიცვალა და ჩემთვის ერთი ფრიად საინტერესო აღმოჩენა გავაკეთე.

ასეთი რამ გინახავათ?

<div onchange="handleEvent()">
//some other markup
</div>

დამეთანხმებით რომ ერთი შეხედვით ამ ივენთის DIV ელემენტთან გამოყენება ცოტა არ იყოს უცნაურია ხომ? რა ცვლილება უნდა მოხდეს ისეთი DIV ელემენტში რომ მისი დამუშავება მოვახდინოთ ამგვარი გზით? ან დავუშვათ და მოხდა ასეთი ცვლილება მაგრამ რანაირად?

მინდა გაგახაროთ და გითხრათ, რომ, პასუხი მარტივია. DIV ელემენტში არანაირი ასეთი ცვლილება არ მოხდება, მაგრამ, თუ მის შიგნით გვაქვს რადმენიმე SELECT და INPUT ელემენტი რომელთა ცვლილების კონტროლი გვესაჭიროება ეს კოდი მომენტალურად იდეალურ გადაწყვეტად იქცევა!

კლასიკურად უფრო სწორად ძველი სკოლის მიდგომით ასეთი ამოცანის გადაჭრის დროს ყველა, SELECT და INPUT ელემენტის onchange ივენთს მივანიჭებდით ჰენდლერს. თუმცა თუ გავიხსენებთ ფაქტს რომ DOM დოკუმენტში ხდომილება ვრცელდება საწყისი ელემენტისგან(ელემენტი რომელზეც უშუალოდ მოხდა ესა თუ ის მოვლენა) ანუ ელემენტების იერარქიაში ქვევიდან ზევით, ჩემს მიერ ნაჩვენები კოდიც მომენტალურად პრაქტიკულ დანიშნულებას შეიძენს. ამ ინფორმაციაზე დაყრდნობით შესაძლებელია ასეთი კოდის დაწერა:

1
2
3
4
5
6
7
8
9
10
<div id="container">
   <input type="text" name="f1" />
   <br />
   <input type="text" name="f2" />
   <br />
   <select name="s1">
       <option value="v1">Value 1</option>
       <option value="v2">Value 2</option>
   </select>
</div>

ნაჩვენები კოდის შესაბამისი ამოცანა მდგომარეობს ყველა INPUT და SELECT ელემენტის onchange ივენთის დაჭერასა და შესაბამის დამუშავებაში. ნაცვლად იმისა რომ გამოვიყენოთ სათითაოდ ყველა ელემენტს onchange ივენთი, მარტივად შეგვიძლია გავაკეთოთ შემდეგი რამ:

1
2
3
4
5
6
7
8
9
10
11
12
13
document.getElementById('container').onchage = function(e) {
   
   //მოვიპოვოთ Event ობიექტი
   e = e || window.event;
   
   //მოვიპოვოთ საწყისი ელემენტი რომელზეც უშუალოდ დაფიქსირდა ივენთი
   var targetElement = e.target || e.srcElement;

   //თუ ელემენტი არის SELECT ან INPUT ტიპის შევასრულოთ დანარჩენი სამუშაო
   if (/^input|select$/i.test(target.nodeName)) {
       //do something here
   }
}

დამეთანხმებით რომ ძალიან მარტივია ყველა ელემენტის ცვლილების ამდაგვარი ცენტრალიზებული კონტროლი. ეს არის ზუსტად ე.წ. Event Delegation მოდელი რომელიც ძალიან პოპულარულია დღესდღეობით. რატომ არ ვიცი მაგრამ ჩემთვის onchange ივენთი იმდენად ცალსახად იყო ასოცირებული ფორმის SELECT ელემენტთან რომ მისი ამდაგვარი გზით გამოყენების შესახებ არასოდეს მიფიქრია, თუმცა საბოლოო შედეგმა ყოველგვარ მოლოდინს გადააჭარბა.

იმედია ეს პირადად ჩემთვის ჭკუის სასწავლებელი შემთხვევა თქვენთვისაც სასარგებლო იქნება.

enjoy :)

ტეგები: , ,

JavaScript ფაილების ჩატვირთვა საჭიროების მიხედვით

თანამედროვე ე.წ. RIA ვებ აპლიკაციებში, კლიენტის მხარეს განსაკუთრებული და საპასუხისმგებლო როლი აკისრია. სწორედ კლიენტის მხარეს იყრის თავს უამრავი CSS/JavaScript და სხვადასხვა გრაფიკული ელემენტები. RIA ტიპისი სისტემებში JavaScript – ს განსაკუთრებული როლი უჭირავს, რადგან სწორედ მისი მეშვეობით მიიღწევა აპლიკაციის განსაკუთრებული და მიმზიდველი ინტერქატიულობა. რაც უფრო მეტად არის დატვირთული მსგავსი ტიპის ელემენტებით აპლიკაცია მით უფრო მეტ რესურსს საჭიროებს იგი.

ერთი შეხედვით ამ ტიპის რესურსები(იგულისხმება ფაილების რაოდენობა, ზომა და ა.შ.) არ უნდა იწვედნენ განსაკუთრებულ სირთულეს, არც ჩატვირთვისა და არც სისწრაფის მიხედვით… თუმცა ეს მხოლოდ ერთი შეხედვით ჩანს ასე.

Yahoo! – ს ინჟინრებმა საკმაოდ საფუძვლიანი კვლევა ჩაატარეს ამ თემასთან დაკავშირებით და აღმოჩნდა რომ სერვერის გადატვირთვას სწორედ, დოკუმენტში ჭარბად გამოყენებული მსგავსი ელემენტები იწვევენ. გარდა სერვერის დატვირთვისა ეს თავისთავად ცხადია უარყოფითად აისახება თავად აპლიკაციის მომხმარებლებზე(ჩატვირთვის სისწრაფე, კოდის შესრულების/ინტერპრეტაციის დრო). სრულად »

ტეგები: , ,

IE incomplete DOM support…

ამჯერად ყურადღებას შევაჩერებ DOM ელემენტის hasAttribute მეთოდზე. ამ მეთოდის სახელი საკმაოდ თვითდოკუმენტირებულია და ადვილი მისახვედრია რისთვისაც გამოიყენება.

ზუსტი განმარტება:

The hasAttribute() method returns TRUE if the current element node has the attribute specified by name, and FALSE otherwise.

მაგრამ IE – ში DOM ელემენტს არ გააჩნია ეს შედარებით იშვიათად გამოყენებადი მაგრამ ძალიან სასარგებლო მეთოდი. თუმცა არსებობს სხვა საშუალება ამ ბრაუზერისათვის რომლის მეშვეობითაც ადვილად ავიცილებთ თავიდან ამ DOM – ის არასრული მხარდაჭერით გამოწვეულ პრობლემას. სრულად »

ტეგები: , ,