Μια ιστοσελίδα γραμμένη σε HTML, όπως την ερμηνεύει ένας φυλλομετρητής αποκτά κάποια συγκεκριμένα χαρακτηριστικά που μπορούν έτσι να μας βοηθήσουν να αποκτήσουμε πρόσβαση στα στοιχεία της μέσω της Javascript. Συγκεκριμένα κάθε tag της html γίνεται ένα οπτικό αντικείμενο στο παράθυρο του φυλλομετρητή, το λεγόμενο και ως DOM object από τα Document Object Model.

Το DOM είναι η θεμελιωμένη προτυποποίηση αναπαράστασης και χρήσης των περιεχομένων ενός HTML ή XML εγγράφου. Το πρότυπο αυτό δεν είναι περίπλοκο αλλά χρειάζεται εκτενή επεξήγηση προκειμένου να το κατανοήσετε. Αρχικά θα πρέπει να καταλάβετε τα εμφωλευμένα στοιχεία (κυρίως tags) ενός εγγράφου HTML/XML όπως αναπαριστώνται μέσω του DOM. 

Επιλογή αντικείμενου με βάση την θέση του στο δέντρο

Στο DOM κάθε έγγραφο αναπαρίστανται σαν ένα δέντρο, όπου κορυφή του είναι το στοιχείο «Document» και κάτω από αυτό υπάρχουν όλα τα στοιχεία που υπάρχουν και μέσα στο κάθε έγγραφο. Κάθε tag αποτελεί και έναν κόμβο του δέντρου, το οποίο αντίστοιχα αν έχει άλλα εμφωλευμένα tags θα διαθέτει κι άλλους κόμβους κάτω από αυτό. Ένας κόμβος μπορεί να είναι ένα tag ή ένα κείμενο, ή ακόμη και τα σχόλια σε HTML. Ας δούμε ένα παράδειγμα:

Αρχείο: dom.html

<!DOCTYPE HTML>
<html>
 <head>
  <title>Στοιχεία DOM</title>
 </head>
 <body>
  <h1>Ένα έγγραφο HTML</h1>
  <p>Αυτό είναι ένα <b>απλό</b> έγγραφο.</p>
 </body>
</html>

Αποτέλεσμα:

Εάν δεν είστε εξοικειωμένοι με τέτοια δέντρα, τότε θα πρέπει να ξέρετε πως μοιάζουν αρκετά με τα γενεαλογικά δέντρα, όπου ένας γονιός έχεις παιδιά, τα παιδιά του γονιού γίνονται γονείς για τα δικά τους παιδιά κ.ο.κ. Άρα παραπάνω το στοιχείο «<p>» είναι παιδί του «<body>» και αδερφάκι του «<h1>» καθώς επίσης έχει τα παιδιά «Αυτό είναι ένα», «<b>» και «έγγραφο». Το «απλό» είναι εγγόνι του «<p>» και παιδί του «<b>».

Με βάση όλα τα παραπάνω ισχύουν κάποιες ετικέτες για κάθε κόμβο με βάση την θέση του μέσα στο δέντρο. Πιο συγκεκριμένα:

  • parentNode
    Είναι ο κόμβος-πατέρας στον κόμβο τον οποίο βρισκόμαστε την εκάστοτε στιγμή. Αν βρισκόμαστε σε ένα στοιχείο που δεν έχει πατέρα όπως το Document τότε το parentNode είναι null.
  • childNodes
    Ένας πίνακας μόνο προς ανάγνωση με όλα τα αντικείμενα τα οποία είναι παιδία του εκάστοτε κόμβου.
  • firstChild, lastChild
    Το πρώτο ή το τελευταίο παιδί ενός κόμβου που βρισκόμαστε εκείνη την στιγμή. Αν κόμβος δεν έχει παιδιά εμφανίζεται η τιμή null.
  • nextSibling, previousSibling
    Ο επόμενος ή ο προηγούμενος αδερφός ενός κόμβου. Δυο ή και παραπάνω παιδία ενός και μόνο κόμβου είναι αδέρφια.
  • nodeType
    Ο τύπος του κόμβου που εξετάζουμε εκείνη την στιγμή. Οι κόμβοι εγγράφου έχουν την τιμή 9. Οι κόμβοι στοιχείων έχουν την τιμή 1. Οι κόμβοι κειμένου έχουν την τιμή 3. Οι κόμβοι σχόλιων έχουν την τιμή 8 και οι κόμβοι τεμαχισμένου εγγράφου την τιμή 11.
  • nodeValue
    Το περιεχόμενο ενός κόμβου κειμένου ή σχόλιου.
  • nodeName
    Το όνομα του tag ενός στοιχείου που αποτελεί τον κόμβο σε κεφαλαία.

Για να χρησιμοποιήσουμε μία από τις παραπάνω ιδιότητες θα πρέπει να βρισκόμαστε ήδη σε ένα συγκεκριμένο κόμβο-στοιχείο. Το πιο κοινό και βασικό κόμβος-στοιχείο είναι το «document» (στοιχείο ρίζα) από όπου ξεκινούν και όλα. 

Ας δούμε ένα παράδειγμα χρησιμοποιώντας το document ως κόμβο-στοιχείο που θα βασιστούμε, εδώ θα πρέπει να σημειώσουμε ότι αν ο κώδικας html διαθέτει τις περιττές αλλαγές γραμμών και τα περριτά κενά για να είναι πιο ευανάγνωστος ο κώδικας, τότε είναι πιθανό οι κόμβοι να είναι κατά πολύ περισσότεροι λόγω του ότι κάθε κενό στην αλλαγή γραμμής θεωρείται διαφορετικός κόμβος. 

Αρχείο: dom_doc.html

<!DOCTYPE HTML><html><head><title>Στοιχεία DOM</title><meta charset='utf-8'/></head><body><h1>Ένα έγγραφο HTML</h1><p>Αυτό είναι ένα <b>απλό</b> έγγραφο.</p></body><script>
console.log(document.childNodes[0].childNodes[1]);
console.log(document.firstChild.firstChild.nextSibling);
</script></html>

Όπως βλέπετε ο κώδικας δεν είναι ευανάγνωστος αλλά μας παρέχει μια συμπιεσμένη μορφή του κώδικα, αποφεύγοντας έτσι τα περιττά κενά, τις περιττές αλλαγές γραμμών, και άλλα σχετικά. Το αποτέλεσμα έχει ως εξής:

Επιλέγεται και στις δυο περιπτώσεις το στοιχείο «<body>» το οποίο είναι το δεύτερο παιδί του «<html>» το οποίο html είναι το πρώτο παιδί του «document». Προσεγγίζουμε το ίδιον κόμβο με διαφορετική χρήση ετικετών. 

Για να αντιμετωπίσουμε το πρόβλημα των περιττών κενών και των αλλαγών γραμμών διατηρώντας ταυτόχρονα την μορφή του κώδικα όπως έχει, αντί να προσπελάσουμε τα στοιχεία ως κόμβους, θα τα προσπελάσουμε ως DOM Elements (αντικείμενα DOM). Είναι ακριβώς το ίδιο πράγμα με προηγουμένως, μόνο που απορρίπτονται όλα τα στοιχεία απλού κειμένου και επιλέγονται μόνο τα tags.  Για να το καταφέρουμε αυτό θα χρησιμοποιήσουμε κάποιες από τις παρακάτω ιδιότητες:

  • firstElementChild, lastElementChild
    Επιστρέφει το πρώτο ή το τελευταίο DOM Αντικείμενο-παιδί ενός κόμβου που έχουμε επιλεγμένο εκείνη την στιγμή.
  • nextElementSibling, previousElementSibling
    Επιστρέφει το επόμενο ή το προηγούμενο DOM Αντικείμενο-συγγενή ενός κόμβου που έχουμε επιλεγμένο εκείνη την στιγμή.
  • childElementCount
    Επιστρέφεται το πλήθος των DOM Αντικειμένων-παιδιών ενός κόμβου που έχουμε επιλεγμένο εκείνη την στιγμή. 

Ας δούμε το ίδιο παράδειγμα με πριν με μορφοποιημένο κώδικα. Στις δυο πρώτες γραμμές της κονσόλας θα ζητήσουμε τα παιδιά-κόμβους, ενώ στις δυο επόμενους τα παιδία-DOM αντικείμενα. 

Αρχείο: dom_doc2.html

<!DOCTYPE HTML>
<html>
 <head>
  <title>Στοιχεία DOM</title>
  <meta charset='utf-8'/>
 </head>
 <body>
  <h1>Ένα έγγραφο HTML</h1>
  <p>Αυτό είναι ένα <b>απλό</b> έγγραφο.</p>
 </body>
 <script>
  console.log(document.childNodes[0].childNodes[1]);
  console.log(document.firstChild.firstChild.nextSibling);
  console.log("--------");
  console.log(document.firstElementChild.lastElementChild);
  console.log(document.firstElementChild.firstElementChild.nextElementSibling);
 </script>
</html>

Αποτέλεσμα: