Promena teme dugmetom
Napravi dugme „Tamna tema“ koje prebacuje stranicu u tamnu temu i nazad, a tekst dugmeta se menja u „Svetla tema“ i obrnuto. Izbor se pamti u localStorage-u.
Pregledač od HTML-a pravi stablo objekata – DOM. JavaScript preko DOM-a pronalazi elemente, menja tekst, stil i klase, pravi nove elemente i reaguje na klikove i unos. Na kraju lekcije pravimo malu aplikaciju „Lista zadataka“ koja pamti podatke i posle zatvaranja pregledača.
document
└── html
├── head
│ └── title → "Lista"
└── body
├── h1 → "Moji zadaci"
├── input#novi
├── button#dodaj
└── ul#lista
├── li → "Nauči DOM"
└── li → "Uradi domaći"
| Pronalaženje elemenata | Vraća |
|---|---|
document.getElementById("lista") | element sa id="lista" |
document.querySelector(".kartica") | prvi element koji odgovara CSS selektoru |
document.querySelectorAll("li") | sve takve elemente (lista po kojoj se ide sa for...of) |
<style>
.tamno { background: #222; color: #eee; }
.kutija { padding: 12px; border-radius: 8px; transition: background 0.3s; }
</style>
<div id="k" class="kutija">
<h3 id="naslov">Naslov</h3>
<p>Klikni dugmad:</p>
<button id="b1">Promeni tekst</button>
<button id="b2">Tamna tema</button>
<button id="b3">Dodaj stavku</button>
<ul id="lista"></ul>
</div>
<script>
const naslov = document.getElementById("naslov");
const kutija = document.getElementById("k");
const lista = document.getElementById("lista");
let n = 0;
document.getElementById("b1").addEventListener("click", () => {
naslov.textContent = "Tekst promenjen u " + new Date().toLocaleTimeString("sr");
});
document.getElementById("b2").addEventListener("click", () => {
kutija.classList.toggle("tamno"); // dodaj klasu ako je nema, ukloni ako je ima
});
document.getElementById("b3").addEventListener("click", () => {
n++;
const li = document.createElement("li"); // novi element
li.textContent = "Stavka " + n;
lista.appendChild(li); // ubaci ga u listu
});
</script>
Rezultat:
| Naredba | Šta radi |
|---|---|
el.textContent = "..." | menja tekst elementa |
el.style.color = "red" | menja jedno CSS svojstvo (imena sa crticom pišu se spojeno: backgroundColor) |
el.classList.add / remove / toggle("ime") | dodaje, uklanja, prebacuje klasu – bolje od menjanja stila jedan po jedan |
document.createElement("li") + roditelj.appendChild(el) | pravi i ubacuje novi element |
el.remove() | briše element |
input.value | tekst upisan u polje |
textContent, a ne sa innerHTML. Sa innerHTML bi se upisani <script> ili <img onerror=...> izvršio – to je XSS napad iz lekcije Bezbednost web aplikacija.Događaj (event) je nešto što se desi na stranici. Funkcija koja se poziva kada se događaj desi zove se osluškivač (event listener):
element.addEventListener("click", function (e) {
// e = podaci o događaju (npr. e.key za taster, e.target za element)
});
| Događaj | Kada se desi |
|---|---|
click | klik mišem ili dodir |
input | svaka promena teksta u polju |
change | promena izbora (select, checkbox) |
submit | slanje forme |
keydown | pritisak tastera (e.key = "Enter", "ArrowUp"…) |
mouseover / mouseout | miš ulazi / izlazi |
DOMContentLoaded | HTML je učitan (umesto ovoga može defer) |
<label>Upiši ime: <input id="ime" type="text"></label>
<p id="pozdrav">Zdravo, ...!</p>
<label>Broj: <input id="broj" type="number" value="5"></label>
<p id="kvadrat"></p>
<script>
const ime = document.getElementById("ime");
ime.addEventListener("input", () => {
document.getElementById("pozdrav").textContent = "Zdravo, " + (ime.value || "...") + "!";
});
const broj = document.getElementById("broj");
function racunaj() {
const x = Number(broj.value); // tekst iz polja -> broj
document.getElementById("kvadrat").textContent = `${x}² = ${x * x}`;
}
broj.addEventListener("input", racunaj);
racunaj(); // izračunaj odmah na početku
</script>
Rezultat:
<form id="f">
<label>Stranica a: <input name="a" type="number" required></label>
<label>Stranica b: <input name="b" type="number" required></label>
<button>Izračunaj</button>
</form>
<p id="rez"></p>
<script>
document.getElementById("f").addEventListener("submit", e => {
e.preventDefault(); // NE šalji formu serveru i ne osvežavaj stranicu
const a = Number(e.target.a.value);
const b = Number(e.target.b.value);
if (a <= 0 || b <= 0) {
document.getElementById("rez").textContent = "Stranice moraju biti pozitivne.";
return;
}
document.getElementById("rez").textContent = `Obim: ${2 * (a + b)}, površina: ${a * b}`;
});
</script>
Rezultat:
e.preventDefault() zaustavlja podrazumevano ponašanje (slanje forme i osvežavanje stranice) – tako JavaScript sam obradi podatke.
<style>
body { font-family: system-ui, Arial, sans-serif; }
.unos { display: flex; gap: 6px; }
.unos input { flex: 1; padding: 6px; }
ul { padding: 0; list-style: none; }
li { display: flex; align-items: center; gap: 8px; padding: 6px; border-bottom: 1px solid #ddd; }
li span { flex: 1; cursor: pointer; }
li.gotovo span { text-decoration: line-through; color: #999; }
li button { border: 0; background: none; cursor: pointer; font-size: 1.1rem; }
</style>
<h3>Moji zadaci</h3>
<form class="unos" id="forma">
<input id="novi" placeholder="Novi zadatak..." autocomplete="off">
<button>Dodaj</button>
</form>
<ul id="lista"></ul>
<p id="brojac"></p>
<script>
// podaci: niz objekata { tekst, gotovo }
let zadaci = [];
try { zadaci = JSON.parse(localStorage.getItem("zadaci")) || []; } catch (e) {}
function sacuvaj() {
try { localStorage.setItem("zadaci", JSON.stringify(zadaci)); } catch (e) {}
}
function prikazi() {
const lista = document.getElementById("lista");
lista.innerHTML = ""; // obriši staru listu (bez korisničkog teksta)
zadaci.forEach((z, i) => {
const li = document.createElement("li");
if (z.gotovo) li.classList.add("gotovo");
const tekst = document.createElement("span");
tekst.textContent = z.tekst; // textContent – bezbedno
tekst.addEventListener("click", () => { z.gotovo = !z.gotovo; sacuvaj(); prikazi(); });
const obrisi = document.createElement("button");
obrisi.textContent = "✕";
obrisi.title = "Obriši";
obrisi.addEventListener("click", () => { zadaci.splice(i, 1); sacuvaj(); prikazi(); });
li.append(tekst, obrisi);
lista.appendChild(li);
});
const ostalo = zadaci.filter(z => !z.gotovo).length;
document.getElementById("brojac").textContent = `Preostalo: ${ostalo} od ${zadaci.length}`;
}
document.getElementById("forma").addEventListener("submit", e => {
e.preventDefault();
const polje = document.getElementById("novi");
const tekst = polje.value.trim();
if (tekst === "") return;
zadaci.push({ tekst: tekst, gotovo: false });
polje.value = "";
sacuvaj();
prikazi();
});
prikazi();
</script>
Rezultat:
Ključne ideje:
zadaci; posle svake promene funkcija prikazi() ponovo nacrta listu iz niza;JSON.stringify, a nazad sa JSON.parse (isti JSON kao u lekciji API i JSON);Ova aplikacija je ista ideja kao To-do aplikacija u Flask-u, samo što tamo podatke čuva server.
Za svaki zadatak postoji primer, uputstvo i rešenje. Rešenja su provereno ispravna u pregledaču.
Napravi dugme „Tamna tema“ koje prebacuje stranicu u tamnu temu i nazad, a tekst dugmeta se menja u „Svetla tema“ i obrnuto. Izbor se pamti u localStorage-u.
Računar zamisli broj od 1 do 100. Korisnik upisuje pokušaj u polje i klikne „Proveri“; ispod se ispisuje „Više“, „Manje“ ili „Pogodak u N pokušaja!“. Dugme „Nova igra“ zamišlja novi broj.
null – stavi <script> na kraj body ili koristi defer.Number().e.preventDefault(), inače se stranica osveži.textContent, ne sa innerHTML.1. Šta vraća document.querySelector(".kartica")?
2. Kako bezbedno upisati tekst koji je uneo korisnik u element?
3. Čemu služi e.preventDefault() u događaju submit?
4. Gde localStorage čuva podatke?