Klijentske web tehnologije

Vežbe | 10. termin

DOM

The Document Object Model (DOM) is the data representation of the objects that comprise the structure and content of a document on the web

Rad sa elementima

Uzimanje postojeceg elementa

Dobijanje reference na element sa datim id-jem:

let element = document.getElementById("id-1")

Dobijanje niza elemenata predstavljanih datim tagom:

let urls = document.getElementsByTagName("a")

Dobijanje niza elemenata koji odgovaraju datom CSS selektoru:

let cbList = document.querySelectorAll('input[type="checkbox"]')

ili jedan element koji odgovara CSS selektoru:

let content = document.querySelector('body > div.main-content')

Kreiranje novog elementa

let novi = document.createElement("button")

Nakon kreiranja element postoji u memoriji, imamo referencu na njega (novi), ali nije ukljucen u strukturu stranice.

Element se ukljucuje u stranicu (document) ubacivanjem u neki drugi element koji je vec na stranici:

stari.appendChild(novi)

ili direktno u body element:

document.body.appendChild(novi)

Takođe, element se moze ukloniti iz roditeljskog elementa (element nastavlja da postoji u memoriji, moze se ponovo dodati u stranicu):

stari.removeChild(novi) // => referenca na uklonjeni elelent (novi)

ili se moze uništiti (više ne postoji u memoriji):

novi.remove()

Referencom na novokreirani element se moze baratati na isti nacin kao i referencom na postojeci:

novi.innerHTML = 'Dugme'
novi.className = 'my-btn-class'
novi.onclick = function() { /* ... */ }
// ...

XHR

XML HTTP Request (XHR)

Sadržaj XML fajla koji ćemo koristiti u primerima:

books.xml

<library>
    <book>
        <title>Title 1<title>
        <author>Author 1<author>
    </book>
    <book>
        <title>Title 2<title>
        <author>Author 2<author>
    </book>
</library>

Slanje HTTP zahteva (sinhrono)

Slanje HTTP zahteva preko koga se dobijaju podaci iz trazenog fajla:

let xhr = new XMLHttpRequest()       // Kreiranje objekta za slanje HTTP zahteva
xhr.open('GET', 'books.xml', false)  // GET   - dobavljanje podataka
xhr.send()                           // false - nije asinhrono (send() blokira dok podaci ne stignu)
xhr.responseXML                      // => XML dokument, sličan HTML dokumentu (document)

u responseXML se nalazi referenca na XML dokument koji sadrži XML elemente, slično kao što HTML dokument (document) sadrži HTML elemente, tako da možemo koristiti DOM funkcije kao što su getElementById, getElementsByTagName i querySelector.

Rad sa dobijenim podacima

Primer čitanja podataka iz dobijenog XML dokument objekta:

let doc = xhr.responseXML                     // XML dokument objekat
let books = doc.getElementsByTagName('book')  // Lista book elemenata

let book = books[0] // Prvi book element

// Pristup child elementima preko DOM funkcija
book.getElementsByTagName('title')[0].textContent   // => "Title 1"
book.getElementsByTagName('author')[0].textContent  // => "Author 1"

// Pristup child elementima preko children niza
book.children[0].nodeName     // => "title"
book.children[0].textContent  // => "Title 1"
book.children[1].nodeName     // => "author"
book.children[1].textContent  // => "Author 1"

Kako bismo lakše baratali podacima iz dobijenog XML dokumenta, možemo ih izvući u obične objekte:

let library = []
for (let book of books) 
{
    let bookObj = {
        title: book.getElementsByTagName('title')[0].textContent,
        author: book.getElementsByTagName('author')[0].textContent,
    }
    library.push(bookObj)
}

Ili možemo automatizovati izvlačenje podataka npr. na ovakav način:

let library = []
for (let book of books) 
{
    let bookObj = {}
    for(let tag of book.children) {
        bookObj[tag.nodeName] = tag.textContent // npr: bookObj["title"] = "Title 1"
    }
    library.push(bookObj)
}

ideja je da za svaki “item” element (book u ovom slučaju) dobijemo elemente (tagove) koji predstavljaju njegove osobine (“title” i “author”) i smestimo njihove vrednosti u objekat koji predstavlja taj jedan “item” (book).

Nakon izvršenog koda niz library će biti:

// library niz:
[
    { title: 'Title 1', author: 'Authro 1' },
    { title: 'Title 2', author: 'Authro 2' }
]

// pristup podacima:
library[0].title  // => "Title 1"
library[0].author // => "Author 1"

Slanje HTTP zahteva (asinhrono)

Prilikom slanja asinhronog HTTP zahteva funkcija send neće blokirati izvršavanje koda, već će kod nastaviti da se izvršava dok se zahtev još uvek obrađuje. To znači da traženi podaci nisu dostupni u XHR objektu u linijama koda nakon send funkcije, već će biti dosupni tek kada se zahtev uspešno završi. Da bismo izvršili kod koji treba da radi sa podacima onda kada se zahtev uspešno izvrši možemo iskoristiti event load. Definišemo funkciju koja će se izvršiti kada se dati event okine:

let xhr = new XMLHttpRequest()

xhr.onload = function() // anonimna funkcija koja će biti izvršena kada poslati HTTP zahtev bude obrađen
{   
    // "this" u ovoj funkciji je referenca na XHR objekat koji je vlasnik ove funkcije
    // (u ovom slučaju to je objekat iza reference koja se čuva u promenljivoj "xhr")
    
    this.responseXML // => XML dokument, sličan HTML dokumentu (document)
}

xhr.open('GET', 'books.xml', true)  // true - asinhrono (send() ne blokira izvršavanje koda dok podaci nisu još stigli)
xhr.send()

Pošto HTTP zahtev može da bude neuspešno obrađen (npr. ne postoji traženi XML fajl) potrebno je da proverimo status zahteva. Svaki status ima svoj kod (broj). U našem slučaju možemo samo proveriti da li je kod 200 ili 304, u oba slučaja zahtev je uspešno prošao i možemo pristupiti traženim podacima preko XHR objekta:

xhr.onload = function() 
{
    if (this.status == 200 || this.status == 304) {
        // HTTP zahtev je uspesno obradjen i podaci su sigurno dostupni u responseXML
        this.responseXML
    }
}

Ova dva status koda u kontekstu slanja GET zahteva u ovim primerima znače:

Napomena: Ako isključimo keš u browseru, za uspešne zahteve nećemo dobijati status 304, već samo 200.

Asinhroni zahtev: Primer

Koristićemo podatke iz istog books.xml fajla.

// Globalni podaci
let library = []

// Kreiranje XHR objekta
let xhr = new XMLHttpRequest()     

// Definisanje funkcije za obradu podataka
xhr.onload = function()
{
    // Prekidamo izvršavanje funkcije ukoliko status nije nijedan od ova dva
    if (this.status != 200 && this.status != 304) return 

    // Iz XML dokumenta izvlačimo niz book elemenata
    let doc = this.responseXML 
    let books = doc.getElementsByTagName('book')
    
    // Za svaki book element njegove podatke pakujemo jedan objekat koga dodajemo u niz library
    for (let book of books) {
        library.push({
            title: book.getElementsByTagName('title')[0].textContent,
            author: book.getElementsByTagName('author')[0].textContent,
        })
        
    }
}

// Priprema i slanje zahteva
xhr.open('GET', 'books.xml', true)
xhr.send()

Nakon izvršavanja onload funkcije niz library sadrži podatke koje smo dobili iz XML fajla.