Το Script του Ολλανδού

Κώδικας, πληροφορίες, ερωτήσεις και απαντήσεις σχετικές με την JavaScript.

Συντονιστές: WebDev Moderators, Super-Moderators

Απάντηση
Άβαταρ μέλους
skeftomilos
Script Master
Δημοσιεύσεις: 2888
Εγγραφή: 07 Ιαν 2005 07:22
Τοποθεσία: Αθήνα

Το Script του Ολλανδού

Δημοσίευση από skeftomilos » 16 Αύγ 2005 10:52

Το Script του Ολλανδού

Απ' ό,τι φαίνεται η Ολλανδία έπαψε να βγάζει μόνο τουλίπες. Μια φουρνιά Ολλανδών designers και scripters έχουν κερδίσει παγκόσμια ακτινοβολία προωθώντας τα Web Standards, εισάγοντας καινοτόμες ιδέες, και κυρίως κοιτάζοντας με καθαρό και διεισδυτικό βλέμμα το Web του παρόντος και του μέλλοντος. Πρώτος απ' όλους ο Peter-Paul Koch (προσοχή στην παύλα γιατί θυμώνει!) με το πολύ γνωστό site του (QuirksMode) που δίκαια βρίσκεται στις πρώτες θέσεις των μηχανών αναζήτησης για τον πλούτο των πληροφοριών που προσφέρει σε θέματα JavaScript. Σύμφωνα μάλιστα με όσα γράφει, αυτές τις μέρες πρέπει να ολοκληρώνει τις διακοπές του στη χώρα μας και να γυρίζει στο PC του. Έτερος και μη εξαιρετέος ο Bobby van der Sluis (χωρίς παύλες αυτός!) που δε γράφει πολύ συχνά, αλλά διαβάζοντας τα κείμενά του μπορεί κάποιος να βιώσει στιγμές αποκάλυψης. Αυτό τουλάχιστον συνέβη σε μένα με το εξαιρετικό του άρθρο Ten good practices for writing JavaScript in 2005. Εκτός των άλλων το site του διαθέτει ένα τέτοιο συνδυασμό λιτότητας και ομορφιάς που σπάνια συναντάει κανείς. Και μαζί με άλλους εννιά ακόμα πολύ αξιόλογους designers - scripters σχημάτισαν πρόσφατα τη συμμορία των έντεκα Happy Cloggers!

Σκοπός αυτού του άρθρου είναι να κάνει μερικές παρατηρήσεις με κριτική διάθεση για ένα script του Van der Sluis, που δημοσιεύτηκε πριν δυόμισι μήνες περίπου με τίτλο Unobtrusive dynamic select boxes. Ας δούμε τι λόγο ύπαρξης έχει αυτό το script. Η αφετηρία είναι δύο απλά dropdown menus φτιαγμένα από καθαρή HTML:

Εικόνα

Όπως βλέπετε τα δύο menus συνδέονται μεταξύ τους με μία λογική σχέση. Το δεξί περιέχει μία λίστα προϊόντων ενώ το αριστερό περιέχει τις εταιρίες που παράγουν τα προϊόντα αυτά. Το πρόβλημα είναι ότι ο πελάτης-χρήστης βλέπει τα προϊόντα όλων των εταιριών ανακατεμένα, και δεν έχει τρόπο να τα φιλτράρει κατά εταιρία. Αυτό βέβαια δεν είναι καινούριο πρόβλημα και μπορεί ασφαλώς να λυθεί με server-side κώδικα. Ωστόσο είναι προφανή τα πλεονεκτήματα μίας client-side προσέγγισης με JavaScript, με πρώτο και καλύτερο την άμεση απόκριση στις ενέργειες του χρήστη. Στο χρόνο που θα χρειαζόταν να περιμένει ο χρήστης για να κατέβει μια νέα σελίδα, τώρα θα έχει προλάβει να ανατρέξει στα προϊόντα πολλών εταιριών. Ας δούμε οπτικά τη λειτουργία των dropdown menus όπως την οραματίστηκε ο συγγραφέας:

Εικόνα

Εικόνα

Εικόνα

Δηλαδή επιλέγοντας διαφορετική εταιρία στο αριστερό μενού, αυτόματα αλλάζουν οι επιλογές του δεξιού. Για να πετύχει ο Ολλανδός αυτη τη λειτουργικότητα δεν ήταν διατεθειμένος να κάνει καμιά παραχώρηση. Η σελίδα έπρεπε να είναι προσβάσιμη χωρίς JavaScript (αν και χωρίς δυνατότητες φίλτρου), ο κώδικας HTML να είναι απόλυτα καθαρός χωρίς ίχνος presentational markup ή νησίδων script, και ο κώδικας JavaScript να μπορεί να μεταφερθεί αυτούσιος σε εξωτερικό αρχείο. Τέλος έπρεπε να μην υπάρχουν πουθενά επαναλαμβανόμενα data, κι εδώ ακριβώς είναι που νομίζω ότι η τεχνική του Van der Sluis έχει κάποιες αδυναμίες. Ας αρχίσουμε σιγά-σιγά να κατευθυνόμαστε προς τα ενδότερα, τον κώδικα δηλαδή! Κατ' αρχήν η HTML:

Κώδικας: Επιλογή όλων

<form action="#">
  <select id="pda-brand">
    <option value="select">Select PDA brand...</option>
    <option value="dell">Dell</option>
    <option value="hp">HP</option>
    <option value="palmone">PalmOne</option>
  </select>
  <select id="pda-type">
    <option class="select" value="select">Select PDA type...</option>
    <option class="dell" value="aximx30">Axim X30</option>
    <option class="dell" value="aximx50">Axim X50</option>
    <option class="hp" value="ipaqhx2750">iPAQ hx2750</option>
    <option class="hp" value="ipaqrx3715">iPAQ rx3715</option>
    <option class="hp" value="ipaqrz1710">iPAQ rz1710</option>
    <option class="palmone" value="tungstene2">Tungsten E2</option>
    <option class="palmone" value="tungstent5">Tungsten T5</option>
    <option class="palmone" value="zire72">Zire 72</option>
  </select>
</form>
Μιά πρώτη άμεση παρατήρηση είναι ότι το attribute action="#" είναι περιττό. Η default συμπεριφορά ενός στοιχείου <form> χωρίς attributes είναι να στέλνει τα δεδομένα με τη μέθοδο GET στην ίδια σελίδα. Μιά δεύτερη παρατήρηση είναι ότι αυτό καθαυτό το tag <form> είναι περιττό. Η HTML τυποποίηση δεν επιβάλλει την τοποθέτηση των στοιχείων <select> μέσα σε φόρμες.


Τεχνικές Συσχέτισης

Πάμε τώρα στο ψητό. Βλέπετε τον τρόπο με τον οποίο ο συγγραφέας συνέδεσε τις σχετιζόμενες επιλογές των δύο menus μεταξύ τους. Συγκεκριμένα χρησιμοποίησε κλάσεις, και προσέθεσε σε κάθε προϊόν μία κλάση με όνομα ίδιο με κάποια από τις values των εταιριών. Συμφωνείτε με αυτή την επιλογή; Από τα λεγόμενά του προκύπτει ότι ο ίδιος ο Van der Sluis καταλαβαίνει καλά ότι κινείται σε ολισθηρό έδαφος:
Some people may note that classes are CSS related, however in my opinion they are designed to offer a mechanism to group and select markup and content for multiple purposes.
Κατά την ταπεινή μου γνώμη η επιλογή αυτή είναι επιτρεπτή μόνο σε καταστάσεις άκρας απελπισίας. Η κλάσεις είναι τεχνική παρουσίασης και δεν πρέπει να φέρουν περιεχόμενο. Αλλά η διαφωνία μου δε μπορεί να έχει και μεγάλη βαρύτητα αν δεν έχω να προτείνω κάποια εναλλακτική λύση. Γι αυτό λέω να προτείνω τέσσερις! :D

1) Custom attributes. Υποστηρίζονται από όλους τους σύγχρονους browsers και διαβάζονται ευκολότατα με τη μέθοδο getAttribute():

Κώδικας: Επιλογή όλων

<select id="pda-type">
  <option brand="" value="select">Select PDA type...</option>
  <option brand="dell" value="aximx30">Axim X30</option>
  <option brand="dell" value="aximx50">Axim X50</option>
  <option brand="hp" value="ipaqhx2750">iPAQ hx2750</option>
  ...

<script>
  window.onload = function&#40;&#41; &#123;
    alert&#40;document.getElementById&#40;'pda-type'&#41;.options&#91;1&#93;.getAttribute&#40;'brand'&#41;&#41;
  &#125;
</script>
Εικόνα

Το μόνο μειονέκτημα αυτής της μεθόδου είναι ότι οι XHTML validators θα παραπονεθούν για το άγνωστο attribute brand και θα βγάλουν invalid το έγγραφό μας. Για κάποιους αυτό μπορεί να είναι πρόβλημα, για μένα απλά δείχνει ότι η XHTML δεν είναι καθόλου eXtensible τελικά! Εκτός αν σκοπεύουμε να σκαρώσουμε κι ένα custom DTD για να ικανοποιήσουμε τους validators (Θεός φυλάξει!). Tag soup for ever!

2) Κρυφά στοιχεία. Προσθέτουμε κρυμμένα στοιχεία HTML δίπλα σε αυτό που μας ενδιαφέρει. Μπορούμε πολύ εύκολα να εντοπίσουμε αυτά τα στοιχεία με τη μέθοδο nextSibling(). Sibling σημαίνει αδελφός από μάνα και πατέρα, αμφιθαλής. :P

Κώδικας: Επιλογή όλων

<style>
  #pda-type span &#123; display&#58; none &#125;
</style>

<select id="pda-type">
  <option value="select">Select PDA type...</option><span></span>
  <option value="aximx30">Axim X30</option><span>dell</span>
  <option value="aximx50">Axim X50</option><span>dell</span>
  <option value="ipaqhx2750">iPAQ hx2750</option><span>hp</span>
  ...

<script>
  window.onload = function&#40;&#41; &#123;
    alert&#40;document.getElementById&#40;'pda-type'&#41;.options&#91;1&#93;.nextSibling.innerHTML&#41;
  &#125;
</script>
Εικόνα

Ουπς! Τι έγινε εδώ! :oops: Συνειδητοποιούμε ξαφνικά ότι το <select> δεν είναι σαν τα άλλα elements και δε μπορούμε να βάζουμε μέσα ότι θέλουμε. Οι HTML parsers απλά αγνοούν αυτά τα στοιχεία <span> που προσθέσαμε εσφαλμένα. Δυστυχώς οι browsers δεν είναι μόνο πολύ αυστηροί με το <select>, είναι και πολύ buggy όπως θα δούμε παρακάτω. Αυτό βέβαια δε σημαίνει ότι η τεχνική αυτή είναι κακή, αλλά μόνο ότι δε μπορεί να εφαρμοστεί στην περίπτωση του <select>. Σε άλλες περιπτώσεις μπορεί να αποδειχθεί πολύ χρήσιμη.

3) Σχόλια (comments). Μπορούμε να βάζουμε σχόλια δίπλα στον κώδικα, έτσι δεν είναι; Μετά τα διαβάζουμε πάλι με την nextSibling(), αφού ίσως ελέγξουμε πρώτα ότι έχουμε να κάνουμε πράγματι με ένα DOM element τύπου comment (nodeType == 8 ):

Κώδικας: Επιλογή όλων

<select id="pda-type">
  <option value="select">Select PDA type...</option><!---->
  <option value="aximx30">Axim X30</option><!--dell-->
  <option value="aximx50">Axim X50</option><!--dell-->
  <option value="ipaqhx2750">iPAQ hx2750</option><!--hp-->
  ...

<script>
  window.onload = function&#40;&#41; &#123;
    alert&#40;document.getElementById&#40;'pda-type'&#41;.options&#91;1&#93;.nextSibling.nodeValue&#41;
  &#125;
</script>
Εικόνα

Δουλεύει ωραία στον Firefox, αλλά τόσο ο IE6 όσο και ο Opera φροντίζουν να καθαρίσουν βιολογικά όλα τα HTML comments που με τόση στοργή και φροντίδα προσθέσαμε στο <select>. Άρα πρέπει μάλλον να αποχαιρετήσουμε και αυτή την τεχνική, και μάλιστα για τα καλά καθώς ο IE5 αρνείται να συγκρατήσει τα comments οπουδήποτε και να βρίσκονται στη σελίδα. :(

4) Ομαδοποίηση με optgroup. Μοιάζει να είναι Η λύση στην περίπτωσή μας, γιατί πέρα από την οργάνωση με λογικό τρόπο των δεδομένων, προσφέρει και σημαντική βελτίωση στην παρουσίαση χωρίς JavaScript:

Κώδικας: Επιλογή όλων

<select id="pda-type">
  <option value="select">Select PDA type...</option>
  <optgroup label="Dell">
    <option value="aximx30">Axim X30</option>
    <option value="aximx50">Axim X50</option>
  </optgroup>
  <optgroup label="HP">
    <option value="ipaqhx2750">iPAQ hx2750</option>
    <option value="ipaqrx3715">iPAQ rx3715</option>
    <option value="ipaqrz1710">iPAQ rz1710</option>
  </optgroup>
  <optgroup label="PalmOne">
    <option value="tungstene2">Tungsten E2</option>
    <option value="tungstent5">Tungsten T5</option>
    <option value="zire72">Zire 72</option>
  </optgroup>
</select>
Εικόνα

Καθόλου άσχημα έτσι δεν είναι; :D Προσωπικά είμαι απόλυτα ικανοποιημένος με τη λύση αυτή. Μπορούμε μάλιστα με αυτό τον τρόπο να απαλλαγούμε τελείως από το πρώτο dropdown στο HTML markup καθώς όλα τα δεδομένα βρίσκονται στο δεύτερο dropdown, και μάλιστα χωρίς καμία επανάληψη. Επομένως μπορούμε να αναθέσουμε στη JavaScript να το προσθέσει στη σελίδα με δυναμικό τρόπο. Στην ουσία είναι άχρηστο όταν δεν υπάρχει JavaScript, γιατί στο δεύτερο dropdown φαίνεται καθαρά σε πια εταιρία ανήκει κάθε προϊόν.

Το Script

Ας αφήσουμε όμως τις σκέψεις μας για τα optgroup να αιωρούνται προς το παρόν, και ας επιστρέψουμε στον κώδικα του Bobby van der Sluis. Στο κάτω-κάτω αυτό το άρθρο φέρει τον τίτλο Το Script του Ολλανδού, ας το δούμε επιτέλους!

Κώδικας: Επιλογή όλων

<script type="text/javascript">
  function dynamicSelect&#40;id1, id2&#41; &#123;
    if &#40;document.getElementById && document.getElementsByTagName&#41; &#123;
      var sel1 = document.getElementById&#40;id1&#41;
      var sel2 = document.getElementById&#40;id2&#41;
      var clone = sel2.cloneNode&#40;true&#41;
      var clonedOptions = clone.getElementsByTagName&#40;"option"&#41;
      refreshDynamicSelectOptions&#40;sel1, sel2, clonedOptions&#41;
      sel1.onchange = function&#40;&#41; &#123;
        refreshDynamicSelectOptions&#40;sel1, sel2, clonedOptions&#41;
      &#125;
    &#125;
  &#125;

  function refreshDynamicSelectOptions&#40;sel1, sel2, clonedOptions&#41; &#123;
    while &#40;sel2.options.length&#41; &#123;
      sel2.remove&#40;0&#41;
    &#125;
    var pattern1 = /&#40; |^&#41;&#40;select&#41;&#40; |$&#41;/
    var pattern2 = new RegExp&#40;"&#40; |^&#41;&#40;" + sel1.options&#91;sel1.selectedIndex&#93;.value + "&#41;&#40; |$&#41;"&#41;
    for &#40;var i = 0; i < clonedOptions.length; i++&#41; &#123;
      if &#40;clonedOptions&#91;i&#93;.className.match&#40;pattern1&#41; || clonedOptions&#91;i&#93;.className.match&#40;pattern2&#41;&#41; &#123;
        sel2.appendChild&#40;clonedOptions&#91;i&#93;.cloneNode&#40;true&#41;&#41;
      &#125;
    &#125;
  &#125;

  window.onload = function&#40;&#41; &#123;
    dynamicSelect&#40;"pda-brand", "pda-type"&#41;
  &#125;
</script>
Κατ' αρχήν να πω ότι έχω κάνει μερικές αλλαγές αισθητικής φύσης: μείωση indentation, αφαίρεση του χαρακτήρα τερματισμού εντολής (;), αφαίρεση σχολίων. Μπορείτε να βρείτε τον αυθεντικό κώδικα ανατρέχοντας στο επίμαχο άρθρο, αλλά σκοπός μου εδώ δεν είναι να επαναλάβω τα λόγια του συγγραφέα εξηγώντας τον κώδικα. Άλλωστε το έχει κάνει ο ίδιος με μεγάλη ικανότητα. Σκοπός μου είναι να εντοπίσω αδυναμίες κα ελαττώματα στον κώδικα, με λίγα λόγια να βγάλω τ' άπλυτα του Ολλανδού στη φόρα! Ok, παραδέχομαι ότι δε με ωθούν και τα πιο ευγενικά των κινήτρων, αλλά στο κάτω-κάτω πάντα έχει πλάκα να κάνει κανείς κριτική, ειδικά εκ του ασφαλούς. :P

Η κριτική

Η πρώτη παρατήρηση έχει να κάνει με τον ορισμό δύο ρουτινών ενώ χρήσιμη είναι μόνο η μία. Η δεύτερη ρουτίνα καλείται μόνο από την πρώτη και θα έπρεπε κανονικά να βρίσκεται μέσα της, ενώ με τον τρόπο που όρισε τις ρουτίνες ο συγγραφέας κατάφερε να μολύνει το global namespace με ένα περιττό όνομα. Αν τώρα το script χρησιμοποιηθεί ταυτόχρονα με άλλα στην ίδια σελίδα, δημιουργείται το δυσάρεστο σενάριο να οριστεί από κάποιο άλλο script μία ρουτίνα ή μεταβλητή με το όνομα refreshDynamicSelectOptions, με αποτέλεσμα ένα από τα δύο script να αποτύχει. Αν τώρα πείτε ότι η πιθανότητα να συμβεί μία σύμπτωση ονομάτων με αυτό το όνομα είναι 0,0000001%, θα συμφωνήσω απόλυτα μαζί σας, αλλά δικαιολογήστε παρακαλώ το γεγονός ότι παρόμοιες σκέψεις παραφροσύνης κυνηγούν συνεχώς τους προγραμματιστές σαν τα φαντάσματα. :D Λοιπόν η JavaScript επιτρέπει nested functions, δηλαδή κώδικα σαν αυτό:

Κώδικας: Επιλογή όλων

function a&#40;&#41; &#123;

  function b&#40;&#41; &#123;
  &#125;

  b&#40;&#41;
&#125;
Με αυτό τον τρόπο μόνο η a() προστίθεται στο global namespace, ενώ η b() υπάρχει ως όνομα μόνο εντός της ρουτίνας που την περιέχει. Ωστόσω δεν ισχυρίζομαι ότι ο Ολλανδός scripter αγνοεί αυτή την τεχνική. Γνωρίζει πολύ καλά ότι οι εσωτερικές ρουτίνες στη JavaScript είναι closures, δηλαδή μικρά τέρατα που επεκτείνουν το scope τους έξω από το σώμα τους. Στην πραγματικότητα αυτή η συμπεριφορά είναι πολύ χρήσιμη συνήθως, αλλά δυστυχώς μπορεί πολύ εύκολα να προκαλέσει memory leaks στον IE. Οπότε υπάρχει σύσταση από τη Microsoft για την αποφυγή των closures στις περιπτώσεις που αυτές δεν είναι εντελώς απαραίτητες. Θα μπορούσε πάντως να γίνει και χωρίς επικίνδυνες closures η απόκρυψη του ονόματος της δεύτερης ρουτίνας, αλλά ο τρόπος είναι κάπως ... αποτρόπαιος:

Κώδικας: Επιλογή όλων

var dynamicSelect = &#40;function&#40;&#41; &#123;
  function refreshDynamicSelectOptions&#40;sel1, sel2, clonedOptions&#41; &#123;
    ...
  &#125;
  return function&#40;id1, id2&#41; &#123;
    ...
  &#125;
&#125;&#41;&#40;&#41;
Μπορώ να καταλάβω απόλυτα τον Ολλανδό αν συνειδητά απέρριψε αυτή την προσέγγιση!

Κώδικας: Επιλογή όλων

var pattern1 = /&#40; |^&#41;&#40;select&#41;&#40; |$&#41;/
Εδώ έχουμε μία απλή regular expression, που αναζητεί τη λέξη select μέσα σε ένα κείμενο. Ο Van der Sluis έχει προβλέψει την περίπτωση να βρίσκεται η λέξη στην αρχή, τη μέση ή το τέλος μίας σειράς λέξεων, ή και μόνη της ακόμα. Όμως μία απλούστερη λύση είναι η εξής:

Κώδικας: Επιλογή όλων

var pattern1 = /\b&#40;select&#41;\b/
Η έκφραση \b σημαίνει όριο λέξης οπότε είναι τέλεια γι αυτή την περίπτωση. Επιπλέον θα δούμε ότι παρακάτω ο κώδικας δεν κάνει καθόλου submatching, επομένως οι παρενθέσεις είναι εντελώς περιττές:

Κώδικας: Επιλογή όλων

var pattern1 = /\bselect\b/

Κώδικας: Επιλογή όλων

if &#40;clonedOptions&#91;i&#93;.className.match&#40;pattern1&#41; ...
Εδώ γίνεται χρήση της μεθόδου match() που όλα τα string διαθέτουν, για να γίνει αναζήτηση στο string και να βρεθούν ταιριάσματα με τη regular expression που μόλις είδαμε. Το πρόβλημα είναι ότι η match() κάνει πολύ περισσότερα από το να απαντά απλά με ένα ναι η ένα όχι για την εύρεση ενός ταιριάσματος, και για το script αυτό είναι αρκετό. Επομένως είναι πιο κατάλληλη η μέθοδος test() των regular expressions:

Κώδικας: Επιλογή όλων

if &#40;pattern1.test&#40;clonedOptions&#91;i&#93;.className&#41; ...
Με αυτό τον τρόπο δεν πρόκειται να επιταχύνουμε το script για περισσότερα από λίγα nanoseconds, ωστόσο θα έχουμε ήσυχη τη συνείδησή μας ότι κάναμε το καλύτερο που μπορούσαμε ως προγραμματιστές. Θα απαλλαγούμε για λίγο και από τα φαντάσματα της παραφροσύνης στα οποία αναφέρθηκα προηγούμενα.

Κώδικας: Επιλογή όλων

for &#40;var i = 0; i < clonedOptions.length; i++&#41; &#123;
Εδώ όμως τα πράγματα είναι πολύ σοβαρά, καθώς έχουμε να κάνουμε με σπατάλη CPU της πιο φρικτής μορφής (δηλαδή ένα ή δυό msec το πολύ!). Το πρόβλημα του παραπάνω βρόγχου είναι η συνθήκη ελέγχου που εκτελείται σε κάθε loop:

Κώδικας: Επιλογή όλων

i < clonedOptions.length
Η μεταβλητή clonedOptions περιέχει εδώ ένα αντικείμενο τύπου HTMLCollection, του οποίου η ιδιότητα length είναι υπολογιζόμενη. Δηλαδή σε κάθε loop μετριόνται και ξαναμετριόνται ένα-ένα τα στοιχεία της συλλογής! Να σημειωθεί ότι αυτό συμβαίνει μόνο σε αυτές τις συλλογές, και όχι στα build-in arrays της JavaScript. Επειδή λοιπόν τα φαντάσματα είναι πάντα πίσω μας και μας κυνηγάνε, θα πρέπει να παίρνουμε κάποια μέτρα πριν εμπλακούμε στα γρανάζια ενός βρόγχου:

Κώδικας: Επιλογή όλων

var length = clonedOptions.length
for &#40;var i = 0; i < length; i++&#41; &#123;
Υπάρχει και η εξής λίγο περίεργη προσέγγιση την οποία γενικά προτιμώ:

Κώδικας: Επιλογή όλων

for &#40;var i = 0, element; element = clonedOptions&#91;i&#93;; i++&#41; &#123;
Εδώ κάνουμε δύο πράγματα σε ένα, απόδοση τιμής και έλεγχο. Μόλις η i φτάσει το length, η έκφραση...

Κώδικας: Επιλογή όλων

element = clonedOptions&#91;i&#93;
...θα επιστρέψει undefined, το οποίο για τη JavaScript ισοδυναμεί με false, και ο βρόγχος θα σταματήσει. Να σημειώσω ότι ήμουν εντελώς ανυποψίαστος για τα προβλήματα απόδοσης της ιδιότητας length την εποχή που ο Van der Sluis έγραφε το άρθρο του. Μόλις πρόσφατα συνειδητοποίησα τι συμβαίνει, όταν διάβασα το: Efficient JavaScript code.

Τα Δύσκολα

Ωραία λοιπόν αναπτερώσαμε το ηθικό και την αυτοεκτίμησή μας, λοιδορήσαμε με εμπάθεια το script ενός ανθρώπου που στο κάτω-κάτω είναι απόλυτα λειτουργικό, unobtrusive, standards compliant, browser friendly, bug-free, memory-leak-free, και στην τελική ανάλυση δεν το πληρώσαμε κιόλας! :D Ευτυχώς όμως υπάρχει θεραπεία στην ξιπασιά μας. Τώρα θα προσπαθήσουμε να υλοποιήσουμε την ιδέα με τα optgroup, και τα φτερά μας θα πέσουν τάχιστα. Δηλαδή όχι απλώς θα πέσουν, φτερά και πούπουλα θα γίνουμε γιατί το scripting των στοιχείων <select> είναι σκέτο ναρκοπέδιο από browser bugs.

Θα αναφέρω εν συντομία μερικά από τα bugs που συνάντησα παλεύοντας με το <select>:

- Η ιδιότητα innerHTML των στοιχείων select δε μπορεί να χρησιμοποιηθεί για να αλλάξει το περιεχόμενό τους. Συμβαίνουν άλλα αντ' άλλων σε όλους τους browsers.

- Ο Opera αρνείται πεισματικά να αφαιρέσει τις κεφαλίδες των optgroup από ένα <select>. Ενώ τα elements αφαιρούνται από το DOM tree, οπτικά παραμένουν στοιβαγμένα στην κορυφή της λίστας.

- Ο IE6 παρουσιάζει φοβερό πρόβλημα με την κλωνοποίηση των στοιχείων optgroup. Memory leaks και crashes στην ημερησία
διάταξη.

- Η δυναμική δημιουργία dropdowns με document.createElement('select') προκαλεί προβλήματα εμφάνισης στον IE. Τα menus μπορεί να εμφανιστούν εκτός θέσης υπό ορισμένες συνθήκες.

- Η εντολή new Option() είναι προβληματική στον IE.

- Η μέθοδος select.add() παρουσιάζει ασυμβατότητες από browser σε browser.

- Ίσως και άλλα που τα έχω ξεχάσει. :)

Το καλύτερο που κατάφερα είναι το παρακάτω demo. Δεν τρέχει στον IE5 και o κώδικας είναι για κλάματα. Αλλά δε μου έμειναν άλλα ψυχικά αποθέματα για να προσπαθήσω να το βελτιώσω, πόσο μάλλον που η παραμικρή αλλαγή απαιτεί μετά εξονυχιστικό testing για να αποκλειστεί η περίπτωση να χάλασε κάτι άλλο.

Live Demo

Τελικά μια χαρά είναι το script του Ολλανδού. Νομίζω ότι θα το προτιμήσω! :D
Τελευταία επεξεργασία από το μέλος skeftomilos την 16 Αύγ 2005 12:32, έχει επεξεργασθεί 1 φορά συνολικά.
The pure and simple truth is rarely pure and never simple. Ο μη νους δε σκέπτεται μη σκέψεις για το τίποτα.

Άβαταρ μέλους
cordis
Administrator, [F|H]ounder, [C|S]EO
Δημοσιεύσεις: 27617
Εγγραφή: 09 Οκτ 1999 03:00
Τοποθεσία: Greece
Επικοινωνία:

Το Script του Ολλανδού

Δημοσίευση από cordis » 16 Αύγ 2005 12:31

Πολύ καλό.. :P εγώ πάντως τα έχω αγαπήσει αυτά τα συνδεδεμένα drop down menus.. :)
Δεν απαντάω σε προσωπικά μηνύματα με ερωτήσεις που καλύπτονται από τις ενότητες του forum. Για ο,τι άλλο είμαι εδώ για εσάς.
- follow me @twitter

Cmg__
Δημοσιεύσεις: 1710
Εγγραφή: 29 Μαρ 2005 22:40

Το Script του Ολλανδού

Δημοσίευση από Cmg__ » 21 Αύγ 2005 03:19

This is MAZOHISM!!!!
Ayto einai psaksimo.

Pantws ta xairomai ta tuts toy Skeftomiloy!
Exoyn apo ena link se kathe leksh gia reference panta prospathei na to dei apo oles tis plevres (scripting-mnhmh-browser bugz klp) kai na vrei sto telos thn antikeimenika kalyterh lysh (opoioy ki an einai ayth :P :D )

The usual :clap: :clap: :clap: :clap: :clap: :clap:
kai ta syni8hsmena 1000 Bravo!

Άβαταρ μέλους
skeftomilos
Script Master
Δημοσιεύσεις: 2888
Εγγραφή: 07 Ιαν 2005 07:22
Τοποθεσία: Αθήνα

Το Script του Ολλανδού

Δημοσίευση από skeftomilos » 24 Αύγ 2005 20:06

A miserable translation of the above article can be found here. Don't blame me, blame altavista! :P
The pure and simple truth is rarely pure and never simple. Ο μη νους δε σκέπτεται μη σκέψεις για το τίποτα.

Άβαταρ μέλους
skeftomilos
Script Master
Δημοσιεύσεις: 2888
Εγγραφή: 07 Ιαν 2005 07:22
Τοποθεσία: Αθήνα

Το Script του Ολλανδού

Δημοσίευση από skeftomilos » 27 Αύγ 2005 04:47

Σκέφτηκα να ενημερώσω τον Ολλανδό για την κριτική στο άρθρο του, και δεν άργησε καθόλου να απαντήσει. :) Η απάντησή του έχει ενδιαφέρον και την παραθέτω αυτούσια (με την άδειά του).
Bobby Van Der Sluis έγραψε:Hi Theodor,

Great to hear you like the stuff I write :-)
The HTML standardisation does not impose the placement of elements <select> in forms.
I actually never really thought about that, thanks!
Optgroup
Definately the best solution if you can get away with it! (personally I don't lik dynamic select boxes that much to be honest, clients just very often want them)
Classes versus custom attributes
Both good with me. I stil have found no ground to believe that the class attribute can only be used by CSS. To me DIV, SPAN, ID and CLASS are structural markup that can serve the goal of styling with CSS, applying behavior with DOM scripting or serving as identifier for CMS.

Κώδικας: Επιλογή όλων

function a&#40;&#41; &#123;

  function b&#40;&#41; &#123;
  &#125;

  b&#40;&#41;
&#125;

I like the closure construction better as well. Mark Wubben's (sIFR, Novemberborn) first comments are always about IE memory leaks. To keep the script simple for publication I left the closure out.

Κώδικας: Επιλογή όλων

/&#40; |^&#41;&#40;select&#41;&#40; |$&#41;/
versus

Κώδικας: Επιλογή όλων

/\bselect\b/
I have had some nice discussions about this one: The first option is better, because word boundaries don't include classnames containing "-", like: container-main or nav-global.

Κώδικας: Επιλογή όλων

for &#40;var i = 0; i < clonedOptions.length; i++&#41; &#123;
Did I do that? ;-)

Cheers,

Bobby
Από μένα μία ακόμα παρατήρηση που ξέχασα να κάνω σχετικά με το άρθρο, και συγκεκριμένα τη γραμμή:

Κώδικας: Επιλογή όλων

var pattern2 = new RegExp&#40;"&#40; |^&#41;&#40;" + sel1.options&#91;sel1.selectedIndex&#93;.value + "&#41;&#40; |$&#41;"&#41;
To DOM1 specification ορίζει ότι τα στοιχεία select διαθέτουν την ιδιότητα value που επιστρέφει την value του επιλεγμένου option. Επομένως μπορεί να γραφεί πιο σύντομα έτσι:

Κώδικας: Επιλογή όλων

var pattern2 = new RegExp&#40;"&#40; |^&#41;&#40;" + sel1.value + "&#41;&#40; |$&#41;"&#41;
Δουλεύει τουλάχιστον στους IE5, IE6, Firefox και Opera 8.
The pure and simple truth is rarely pure and never simple. Ο μη νους δε σκέπτεται μη σκέψεις για το τίποτα.

Απάντηση

Επιστροφή στο “JavaScript και Frameworks”

Μέλη σε σύνδεση

Μέλη σε αυτήν τη Δ. Συζήτηση: Δεν υπάρχουν εγγεγραμμένα μέλη και 1 επισκέπτης