DOM (Document Object Model) ile tarayıcı sayfalarını dinamik hale getirirsiniz. Bu derste DOM manipülasyonu, event sistemi, form yönetimi ve modern browser API'lerini derinlemesine inceliyoruz.
DOM Seçimi ve Manipülasyon
// JAVASCRIPT //
// Element seçimi
const baslik = document.querySelector('h1');
const tumButonlar = document.querySelectorAll('button');
const idliEleman = document.getElementById('ana-icerik');
const sinifliEleman = document.getElementsByClassName('kart'); // HTMLCollection
// Element oluşturma ve ekleme
function kartOlustur(veri) {
const kart = document.createElement('div');
kart.className = 'kart bg-white shadow rounded p-4';
kart.dataset.id = veri.id; // data-id="123"
// innerHTML yerine DOM API — XSS güvenliği
const baslik = document.createElement('h2');
baslik.textContent = veri.baslik; // textContent — HTML parse etmez
const aciklama = document.createElement('p');
aciklama.textContent = veri.aciklama;
const buton = document.createElement('button');
buton.textContent = 'Detay';
buton.setAttribute('aria-label', `${veri.baslik} detayını gör`);
kart.append(baslik, aciklama, buton);
return kart;
}
// Element ekleme yöntemleri
const konteyner = document.getElementById('kartlar');
konteyner.append(kartOlustur(veri)); // Sona ekle
konteyner.prepend(kartOlustur(veri)); // Başa ekle
konteyner.insertAdjacentElement('afterbegin', // Diğer seçenekler
kartOlustur(veri));
// Element silme
const silinecek = document.querySelector('.eski');
silinecek?.remove(); // Optional chaining — element yoksa hata vermez
// CSS manipülasyon
const kutu = document.querySelector('.kutu');
kutu.classList.add('aktif');
kutu.classList.remove('pasif');
kutu.classList.toggle('gizli'); // Varsa kaldır, yoksa ekle
kutu.classList.replace('eski', 'yeni');
kutu.style.transform = 'translateX(100px)'; // Inline stil (kaçınılmalı)Event Sistemi
// JAVASCRIPT //
// addEventListener — tercih edilen yöntem
const buton = document.getElementById('gonder-btn');
function tiklamaIsleci(event) {
event.preventDefault(); // Varsayılan davranışı engelle
event.stopPropagation(); // Bubble'ı durdur
console.log('Tıklandı!', event.target, event.currentTarget);
}
buton.addEventListener('click', tiklamaIsleci);
buton.removeEventListener('click', tiklamaIsleci); // Aynı referans!
// Event delegation — toplu listener (performans)
const liste = document.getElementById('urun-listesi');
liste.addEventListener('click', event => {
const buton = event.target.closest('[data-aksiyon]');
if (!buton) return;
const aksiyon = buton.dataset.aksiyon;
const urunId = buton.closest('[data-urun-id]')?.dataset.urunId;
switch (aksiyon) {
case 'sil': urunSil(urunId); break;
case 'duzenle': urunDuzenle(urunId); break;
case 'sepete': sepeteEkle(urunId); break;
}
});
// Keyboard event
document.addEventListener('keydown', event => {
if (event.key === 'Escape') modaliKapat();
if (event.ctrlKey && event.key === 's') { event.preventDefault(); kaydet(); }
if (event.key === 'ArrowDown') sonraki();
});
// Custom event
const ozelEtkinlik = new CustomEvent('urun:guncellendi', {
detail: { urunId: 42, yeniAd: 'Laptop' },
bubbles: true,
});
document.dispatchEvent(ozelEtkinlik);
document.addEventListener('urun:guncellendi', event => {
console.log(event.detail.urunId); // 42
});Form Yönetimi
// JAVASCRIPT //
// Form validasyon ve gönderme
const form = document.getElementById('kayit-formu');
form.addEventListener('submit', async event => {
event.preventDefault();
// FormData API
const veriler = new FormData(form);
const json = Object.fromEntries(veriler.entries()); // {email: "...", sifre: "..."}
// Validasyon
const hatalar = formValidasyonu(json);
if (Object.keys(hatalar).length > 0) {
hatalariGoster(hatalar);
return;
}
// Gönderme durumu
const gonderBtn = form.querySelector('[type="submit"]');
gonderBtn.disabled = true;
gonderBtn.textContent = 'Kaydediliyor...';
try {
const cevap = await fetch('/api/kayit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(json),
});
if (!cevap.ok) throw new Error('Sunucu hatası');
form.reset();
basariMesajiGoster('Kayıt başarılı!');
window.location.href = '/dashboard';
} catch (hata) {
hataGoster(hata.message);
} finally {
gonderBtn.disabled = false;
gonderBtn.textContent = 'Kaydet';
}
});
function formValidasyonu(veriler) {
const hatalar = {};
if (!veriler.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(veriler.email)) {
hatalar.email = 'Geçerli bir e-posta girin';
}
if (!veriler.sifre || veriler.sifre.length < 8) {
hatalar.sifre = 'Şifre en az 8 karakter olmalı';
}
return hatalar;
}
function hatalariGoster(hatalar) {
// Önceki hataları temizle
document.querySelectorAll('.hata-mesaji').forEach(el => el.remove());
Object.entries(hatalar).forEach(([alan, mesaj]) => {
const input = document.querySelector(`[name="${alan}"]`);
if (!input) return;
input.classList.add('border-red-500');
const hataMesaji = document.createElement('p');
hataMesaji.className = 'hata-mesaji text-red-500 text-sm mt-1';
hataMesaji.textContent = mesaj;
input.parentNode.insertBefore(hataMesaji, input.nextSibling);
});
}Intersection Observer — Lazy Loading
// JAVASCRIPT //
// Görünürlük takibi — sayfa içi analitik, lazy load
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('gorunde');
observer.unobserve(entry.target); // Bir kez tetikle
}
});
},
{
threshold: 0.1, // %10 görünürse tetikle
rootMargin: '0px 0px -100px 0px', // Alt sınırı 100px içeri çek
}
);
document.querySelectorAll('.animasyonlu').forEach(el => observer.observe(el));
// Sonsuz scroll
const sonYuklenenRef = document.getElementById('son-eleman');
const scrollObserver = new IntersectionObserver(async ([entry]) => {
if (!entry.isIntersecting || yukleniyorRef.current) return;
yukleniyorRef.current = true;
const yeniOgeler = await daha_fazla_getir();
listeye_ekle(yeniOgeler);
yukleniyorRef.current = false;
});
scrollObserver.observe(sonYuklenenRef);MutationObserver — DOM Değişikliği Takibi
// JAVASCRIPT //
const targetNode = document.getElementById('dinamik-icerik');
const observer = new MutationObserver(mutations => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
console.log('Yeni element eklendi:', node);
// Yeni elemana event listener ekle
if (node.matches('[data-tooltip]')) {
tooltipBagla(node);
}
}
});
}
}
});
observer.observe(targetNode, {
childList: true, // Alt elemanlar
subtree: true, // Tüm alt ağaç
attributes: false,
});
observer.disconnect(); // Takibi durdurResizeObserver ve requestAnimationFrame
// JAVASCRIPT //
// Element boyutu değişim takibi
const resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
console.log(`${entry.target.id}: ${width}x${height}`);
if (width < 600) {
entry.target.classList.add('mobil');
} else {
entry.target.classList.remove('mobil');
}
}
});
resizeObserver.observe(document.querySelector('.esnek-kutu'));
// requestAnimationFrame — akıcı animasyon
function animasyonBaslat(element, hedefX, hedefY) {
let baslangic = null;
const sure = 500; // ms
function adim(timestamp) {
if (!baslangic) baslangic = timestamp;
const ilerleme = Math.min((timestamp - baslangic) / sure, 1);
// Easing — ease-out
const kolaylestirilmis = 1 - Math.pow(1 - ilerleme, 3);
element.style.transform = `translate(${hedefX * kolaylestirilmis}px, ${hedefY * kolaylestirilmis}px)`;
if (ilerleme < 1) {
requestAnimationFrame(adim);
}
}
requestAnimationFrame(adim);
}Clipboard ve Navigator API
// JAVASCRIPT //
// Modern Clipboard API
async function kopyala(metin) {
try {
await navigator.clipboard.writeText(metin);
bildirimGoster('Kopyalandı!');
} catch {
// Fallback — eski yöntem
const gecici = document.createElement('textarea');
gecici.value = metin;
document.body.appendChild(gecici);
gecici.select();
document.execCommand('copy');
gecici.remove();
}
}
// Geolocation
navigator.geolocation.getCurrentPosition(
konum => {
console.log(konum.coords.latitude, konum.coords.longitude);
haritaGoster(konum.coords);
},
hata => console.warn('Konum alınamadı:', hata.message),
{ enableHighAccuracy: true, timeout: 10000 }
);
// Visibility API — sayfa görünürlüğü
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
videoRef?.pause();
} else {
videoRef?.play();
}
});Sonuç
DOM API'si ile gerçek zamanlı arayüz güncellemeleri, event delegation ile performanslı olay yönetimi, Observer API'leri ile akıllı izleme ve modern browser API'leri ile kullanıcı deneyimi zenginleştirme; modern JavaScript geliştiricisinin temel becerileridir. Bir sonraki derste JavaScript tasarım desenleri ve mimarisi konularını ele alacağız.