DOM Üzerinde Değişiklik Yapma Rehberi: JavaScript ile Etkileşimli Web Sayfaları Oluşturma
Giriş: DOM Nedir ve Neden Önemlidir?
Web sayfaları, kullanıcılarla etkileşime giren dinamik arayüzler sunmak için sürekli olarak güncellenir ve değiştirilir. Bu dinamizmi sağlayan temel mekanizma, Belge Nesne Modeli (Document Object Model – DOM) ve JavaScript’in bu model üzerinde yaptığı manipülasyonlardır. Bir web sayfası yüklendiğinde, tarayıcı HTML kodunu ayrıştırır ve bu kodun her bir bölümünü bir nesne olarak temsil eden bir ağaç yapısı oluşturur. Bu ağaç yapısı, DOM olarak adlandırılır. DOM, JavaScript gibi programlama dillerinin, sayfanın içeriğini, yapısını ve stilini dinamik olarak değiştirmesine olanak tanıyan bir arayüz sağlar.
DOM’un Tanımı
DOM, bir HTML veya XML belgesinin programatik bir temsilidir. Belgedeki her eleman (etiket, metin, nitelik vb.) bir “düğüm” (node) olarak temsil edilir. Bu düğümler, hiyerarşik bir ağaç yapısı içinde düzenlenir. En üstteki düğüm, document nesnesidir ve tüm diğer elemanlar bu nesnenin altındadır. DOM, web geliştiricilerine JavaScript aracılığıyla bu düğümlere erişme, onları değiştirme, yeni düğümler oluşturma ve mevcut düğümleri silme yeteneği sunar. Bu sayede, kullanıcı etkileşimlerine yanıt veren, verileri dinamik olarak yükleyen veya sayfa içeriğini anında güncelleyen web uygulamaları geliştirmek mümkün hale gelir.
DOM’un Yapısı: Ağaç Modeli
DOM, bir ağaç yapısı olarak görselleştirilebilir. Bu yapının en tepesinde document nesnesi bulunur. document nesnesinin altında, etiketiyle temsil edilen kök eleman yer alır. elemanının altında ise ve gibi elemanlar bulunur. Her eleman, kendi içinde başka elemanları (çocukları) barındırabilir ve aynı seviyede başka elemanlarla (kardeşleri) birlikte bulunabilir. Örneğin, bir
etiketi olabilir ve bu
etiketleri birbirinin kardeşi olurken,
etiketi de onların ebeveyni olur. Bu hiyerarşik yapı, JavaScript’in belirli bir elemanı hedeflemesini, onun çocuklarına veya ebeveynine ulaşmasını ve böylece sayfanın herhangi bir bölümünü manipüle etmesini kolaylaştırır.
Web Geliştirmedeki Rolü
DOM, modern web geliştirmenin temel taşlarından biridir. Kullanıcı etkileşimlerini yönetmek (tıklamalar, form gönderimleri, klavye girişleri), sunucudan gelen verileri sayfaya yansıtmak, animasyonlar ve görsel efektler oluşturmak, form doğrulama yapmak ve genel olarak web sayfalarını statik olmaktan çıkarıp dinamik ve etkileşimli hale getirmek için DOM manipülasyonu kaçınılmazdır. JavaScript, bu manipülasyonları gerçekleştirmek için tarayıcı tarafından sağlanan DOM API’lerini kullanır. Bu makale, JavaScript kullanarak DOM üzerinde nasıl değişiklikler yapılacağını, elemanlara nasıl erişileceğini, yeni elemanların nasıl oluşturulup ekleneceğini, mevcut elemanların nasıl güncellenip silineceğini ve olay dinleyicileriyle nasıl etkileşim kurulacağını detaylı bir şekilde açıklayacaktır.
DOM Elemanlarına Erişme Yöntemleri
DOM üzerinde herhangi bir değişiklik yapmadan önce, hedef elemana erişmemiz gerekir. JavaScript, bu amaçla çeşitli yöntemler sunar. Doğru yöntemi seçmek, performans ve kod okunabilirliği açısından önemlidir.
ID ile Erişme: getElementById()
document.getElementById() yöntemi, bir HTML elemanına benzersiz id niteliği aracılığıyla erişmenin en hızlı ve en doğrudan yoludur. Her id değeri belgede yalnızca bir kez kullanılmalıdır. Eğer belirtilen id‘ye sahip bir eleman bulunamazsa, yöntem null döndürür.
// HTML: Merhaba Dünya
const myDiv = document.getElementById('myDiv');
console.log(myDiv.textContent); // "Merhaba Dünya"
Sınıf Adı ile Erişme: getElementsByClassName()
document.getElementsByClassName() yöntemi, belirli bir sınıf adına sahip tüm elemanları içeren canlı bir HTMLCollection döndürür. “Canlı” olması, DOM’da yapılan değişikliklerin bu koleksiyonu otomatik olarak güncellemesi anlamına gelir. Eğer eşleşen eleman bulunamazsa boş bir koleksiyon döndürülür.
// HTML: Paragraf 1
Paragraf 2
const paragraphs = document.getElementsByClassName('myClass');
console.log(paragraphs.length); // 2
console.log(paragraphs[0].textContent); // "Paragraf 1"
Etiket Adı ile Erişme: getElementsByTagName()
document.getElementsByTagName() yöntemi, belirtilen etiket adına (örneğin, p, div, a) sahip tüm elemanları içeren canlı bir HTMLCollection döndürür. Tıpkı getElementsByClassName() gibi, bu da canlı bir koleksiyondur.
// HTML: - Öğe 1
- Öğe 2
const listItems = document.getElementsByTagName('li');
console.log(listItems.length); // 2
console.log(listItems[1].textContent); // "Öğe 2"
CSS Seçiciler ile Erişme: querySelector() ve querySelectorAll()
Bu iki yöntem, modern web geliştirmenin en güçlü ve esnek eleman erişim araçlarıdır. CSS seçicilerini kullanarak elemanlara erişmenizi sağlarlar.
* document.querySelector(): Belirtilen CSS seçiciyle eşleşen ilk elemanı döndürür. Eğer eşleşen bir eleman bulunamazsa null döndürür. Tek bir elemana ihtiyacınız olduğunda idealdir.
// HTML: İlk metin
İkinci metin
const firstParagraph = document.querySelector('#container .text');
console.log(firstParagraph.textContent); // "İlk metin"
* document.querySelectorAll(): Belirtilen CSS seçiciyle eşleşen tüm elemanları içeren statik bir NodeList döndürür. “Statik” olması, DOM’da yapılan değişikliklerin bu listeyi otomatik olarak güncellemediği anlamına gelir. Eğer eşleşen eleman bulunamazsa boş bir NodeList döndürülür.
const allParagraphs = document.querySelectorAll('.text');
console.log(allParagraphs.length); // 2
allParagraphs.forEach(p => console.log(p.textContent));
// Çıktı: "İlk metin", "İkinci metin"
querySelector ve querySelectorAll, ID, sınıf, etiket, nitelik seçicileri ve bunların kombinasyonları dahil olmak üzere herhangi bir geçerli CSS seçicisini kullanabilir. Bu, onları elemanlara erişim için son derece esnek kılar.
Traversal Yöntemleri: Ebeveyn, Çocuk, Kardeş Elemanlara Erişim
Bazen doğrudan bir seçici kullanmak yerine, zaten eriştiğimiz bir elemandan yola çıkarak diğer elemanlara (ebeveyn, çocuk veya kardeş) ulaşmak isteyebiliriz. Bu, DOM’un ağaç yapısından faydalanarak yapılır.
* parentNode: Bir elemanın doğrudan ebeveyn düğümünü döndürür.
// HTML: Ben çocuğum
const child = document.getElementById('child');
const parent = child.parentNode;
console.log(parent.id); // "parent"
* children: Bir elemanın tüm çocuk elemanlarını (yalnızca eleman düğümleri, metin düğümleri değil) içeren canlı bir HTMLCollection döndürür.
// HTML: - Öğe 1
- Öğe 2
const myList = document.getElementById('myList');
const children = myList.children;
console.log(children.length); // 2
* firstChild / lastChild: Bir elemanın ilk/son çocuk düğümünü (metin veya yorum düğümleri dahil) döndürür.
firstElementChild / lastElementChild: Bir elemanın ilk/son çocuk eleman* düğümünü döndürür. Genellikle bu ikisi tercih edilir, çünkü metin düğümlerini göz ardı ederler.
// HTML: İlk metin Span Son metin
const container = document.getElementById('container');
console.log(container.firstChild); // Metin düğümü
console.log(container.firstElementChild.tagName); // "SPAN"
nextElementSibling / previousElementSibling: Bir elemanın DOM ağacındaki bir sonraki/önceki kardeş eleman* düğümünü döndürür.
// HTML: Paragraf 1
Paragraf 2
const p1 = document.getElementById('p1');
const p2 = p1.nextElementSibling;
console.log(p2.id); // "p2"
Bu yöntemler, belirli bir elemanın konumuna göre diğer elemanlara erişmek için son derece kullanışlıdır ve karmaşık DOM yapılarında gezinmeyi sağlar.
Yeni DOM Elemanları Oluşturma ve Ekleme
Web sayfalarına dinamik olarak içerik eklemek, DOM manipülasyonunun en yaygın kullanımlarından biridir. JavaScript, yeni HTML elemanları oluşturup bunları mevcut DOM’a eklemek için güçlü araçlar sunar.
Eleman Oluşturma: createElement()
document.createElement() yöntemi, belirtilen etiket adına sahip yeni bir HTML elemanı oluşturur. Bu eleman, henüz DOM’a eklenmemiştir; sadece hafızada bir nesne olarak var olur.
const newDiv = document.createElement('div'); // Yeni bir elemanı oluşturur
const newParagraph = document.createElement('p'); // Yeni bir elemanı oluşturur
Metin Düğümü Oluşturma: createTextNode()
Bir elemanın içine metin eklemek için textContent veya innerHTML kullanılabileceği gibi, doğrudan bir metin düğümü de oluşturulabilir. document.createTextNode() yöntemi, belirtilen metin içeriğine sahip yeni bir metin düğümü oluşturur.
const textNode = document.createTextNode('Bu yeni bir metin.');
Bu metin düğümü daha sonra bir elemana appendChild() ile eklenebilir.
Elemanları Ekleme: appendChild(), insertBefore()
Oluşturulan elemanları veya metin düğümlerini DOM’a eklemek için çeşitli yöntemler bulunur:
* parentNode.appendChild(childNode): Belirtilen childNode‘u, parentNode‘un son çocuğu olarak ekler.
const container = document.getElementById('container'); // Mevcut bir eleman
const newDiv = document.createElement('div');
newDiv.textContent = 'Yeni Div İçeriği';
container.appendChild(newDiv); // container içine newDiv'i ekler
* parentNode.insertBefore(newNode, referenceNode): newNode‘u, parentNode‘un çocukları arasına, referenceNode‘dan hemen önce ekler.
const list = document.getElementById('myList'); // Mevcut bir
const newItem = document.createElement('li');
newItem.textContent = 'Yeni Öğe';
const firstItem = list.firstElementChild; // Listenin ilk öğesi
list.insertBefore(newItem, firstItem); // Yeni öğeyi ilk öğeden önce ekler
* element.append() ve element.prepend(): Bu modern yöntemler, bir elemanın içine birden fazla düğüm veya metin eklemeye olanak tanır. append() sona, prepend() başa ekler.
const container = document.getElementById('container');
const p1 = document.createElement('p');
p1.textContent = 'Paragraf 1';
const p2 = document.createElement('p');
p2.textContent = 'Paragraf 2';
container.append(p1, p2, 'Metin de ekleyebiliriz.'); // Birden fazla eleman ve metin ekler
* element.before() ve element.after(): Bir elemanın kendisinden önce veya sonra kardeş eleman olarak eklemeye yarar.
const existingElement = document.getElementById('existing');
const newSibling = document.createElement('span');
newSibling.textContent = 'Yeni Kardeş';
existingElement.before(newSibling); // existingElement'ten önce ekler
Örnek Senaryo: Dinamik Liste Oluşturma
Bir JavaScript dizisinden dinamik olarak bir liste (ul/li) oluşturma:
// HTML:
const data = ['Elma', 'Armut', 'Muz', 'Çilek'];
const dynamicList = document.getElementById('dynamicList');
data.forEach(itemText => {
const listItem = document.createElement('li'); // Yeni oluştur
listItem.textContent = itemText; // Metin içeriğini ayarla
dynamicList.appendChild(listItem); // 'ye - 'yi ekle
});
// Sonuç:
- Elma
- Armut
- Muz
- Çilek
Mevcut DOM Elemanlarını Güncelleme
Var olan elemanların içeriğini, niteliklerini veya stilini değiştirmek, DOM manipülasyonunun en sık karşılaşılan görevlerinden biridir.
Metin İçeriğini Değiştirme: textContent ve innerHTML
* element.textContent: Bir elemanın ve onun tüm alt elemanlarının sadece metin içeriğini alır veya ayarlar. HTML etiketlerini metin olarak ele alır ve güvenlik açısından daha güvenlidir.
// HTML: Eski Metin
const myParagraph = document.getElementById('myParagraph');
myParagraph.textContent = 'Yeni Metin'; // Yeni Metin
myParagraph.textContent = 'Kalın Metin'; // <b>Kalın Metin</b>
* element.innerHTML: Bir elemanın HTML içeriğini alır veya ayarlar. Bu, sadece metin değil, aynı zamanda HTML etiketlerini de eklemenize olanak tanır. Ancak, kullanıcıdan gelen verilerle kullanıldığında XSS (Cross-Site Scripting) güvenlik açıklarına yol açabilir.
// HTML:
const myDiv = document.getElementById('myDiv');
myDiv.innerHTML = 'Başlık
Yeni İçerik
';
// Sonuç: Başlık
Yeni İçerik
// Güvenlik Notu:
// myDiv.innerHTML = userInput; // Eğer userInput kötü niyetli script içeriyorsa tehlikeli olabilir.
Genel olarak, sadece metin içeriğini değiştirecekseniz textContent‘ı tercih edin. HTML yapısını değiştirmek gerekiyorsa innerHTML‘i dikkatli kullanın veya document.createElement() gibi yöntemlerle elemanları programatik olarak oluşturup ekleyin.
Nitelikleri (Attribute) Değiştirme: setAttribute(), getAttribute(), removeAttribute()
HTML elemanlarının nitelikleri (örneğin, src, href, class, id) JavaScript ile okunabilir, değiştirilebilir veya kaldırılabilir.
* element.setAttribute(name, value): Bir elemanın belirtilen niteliğini ayarlar veya günceller.
// HTML:
const myImage = document.getElementById('myImage');
myImage.setAttribute('src', 'new.jpg');
myImage.setAttribute('alt', 'Yeni Resim');
myImage.setAttribute('width', '200'); // Yeni bir nitelik ekler
* element.getAttribute(name): Bir elemanın belirtilen niteliğinin değerini döndürür.
console.log(myImage.getAttribute('src')); // "new.jpg"
* element.removeAttribute(name): Bir elemanın belirtilen niteliğini kaldırır.
myImage.removeAttribute('width'); // width niteliğini kaldırır
Bazı yaygın nitelikler (id, className, href, src vb.) doğrudan elemanın bir özelliği olarak da erişilebilir ve değiştirilebilir:
myImage.src = 'another.jpg'; // Daha kısa ve yaygın kullanım
console.log(myImage.id);
Stil Değişiklikleri: style Nesnesi ve classList API
Elemanların görünümünü değiştirmek için CSS stilleri manipüle edilebilir.
* Inline Stiller: element.style Nesnesi: Her HTML elemanının bir style özelliği vardır. Bu özellik, elemanın inline CSS stillerini içeren bir CSSStyleDeclaration nesnesidir. JavaScript ile bu nesnenin özelliklerini ayarlayarak doğrudan inline stil uygulayabilirsiniz. CSS özellik adları camelCase formatında yazılır (örneğin, background-color yerine backgroundColor).
const myDiv = document.getElementById('myDiv');
myDiv.style.backgroundColor = 'blue';
myDiv.style.color = 'white';
myDiv.style.fontSize = '18px';
Bu yöntem, tekil stil değişiklikleri için uygundur ancak karmaşık stil yönetimi için önerilmez. Inline stiller, CSS öncelik sıralamasında yüksek bir yere sahiptir ve genellikle daha az esneklik sağlar.
* CSS Sınıfları ile Yönetim: element.classList API: Elemanların stillerini yönetmenin en iyi yolu, CSS sınıflarını kullanmaktır. classList API’si, bir elemanın sınıf listesini kolayca yönetmenizi sağlar.
* element.classList.add(className): Bir veya daha fazla sınıf ekler.
* element.classList.remove(className): Bir veya daha fazla sınıfı kaldırır.
* element.classList.toggle(className): Sınıf varsa kaldırır, yoksa ekler.
* element.classList.contains(className): Belirtilen sınıfın eleman üzerinde olup olmadığını kontrol eder (true/false döndürür).
// CSS: .highlight { background-color: yellow; } .active { color: red; }
const myButton = document.getElementById('myButton');
myButton.classList.add('highlight'); // highlight sınıfını ekler
console.log(myButton.classList.contains('highlight')); // true
myButton.classList.remove('highlight'); // highlight sınıfını kaldırır
console.log(myButton.classList.contains('highlight')); // false
myButton.classList.toggle('active'); // active sınıfını ekler
// Kullanıcı bir daha tıkladığında:
myButton.classList.toggle('active'); // active sınıfını kaldırır
classList API’si, stil değişikliklerini daha modüler ve yönetilebilir hale getirir, çünkü tüm stil tanımlamaları CSS dosyalarında kalır.
DOM Elemanlarını Silme ve Kaldırma
Dinamik web sayfalarında, artık ihtiyaç duyulmayan elemanları DOM’dan kaldırmak da önemlidir. Bu, sayfa performansını artırabilir ve gereksiz bellek kullanımını önleyebilir.
Eleman Silme: parentNode.removeChild(childNode)
Bir elemanı kaldırmak için, onun ebeveynine erişip removeChild() yöntemini çağırmanız gerekir. Bu yöntem, belirtilen çocuk düğümünü DOM’dan kaldırır.
// HTML: Beni kaldır!
const container = document.getElementById('container');
const removableParagraph = document.getElementById('removable');
if (removableParagraph) {
container.removeChild(removableParagraph); // Paragrafı container'dan kaldırır
}
Bu yöntem, kaldırılacak elemanın ebeveynini bilmeyi gerektirir.
Kendini Silme: element.remove()
Modern tarayıcılarda (IE hariç), bir elemanı kendi üzerinden remove() yöntemiyle kaldırmak çok daha kolaydır. Bu yöntem, elemanı doğrudan DOM’dan kaldırır ve ebeveynini bilme gereksinimini ortadan kaldırır.
// HTML: Beni kaldır!
const removableParagraph = document.getElementById('removable');
if (removableParagraph) {
removableParagraph.remove(); // Paragrafı doğrudan kaldırır
}
remove() yöntemi, removeChild()‘a göre daha sezgisel ve kullanımı kolaydır.
Örnek Senaryo: Dinamik Eleman Silme
Bir “Sil” düğmesi tıklandığında, ilgili liste öğesini kaldırma:
// HTML:
//
// - Öğe 1
// - Öğe 2
//
const deleteButtons = document.querySelectorAll('.delete-btn');
deleteButtons.forEach(button => {
button.addEventListener('click', function() {
const listItem = this.parentNode; // Düğmenin ebeveyni ()
listItem.remove(); // elemanını kaldır
});
});
Bu örnekte, this.parentNode kullanarak düğmeye en yakın
elemanına erişip onu remove() metoduyla siliyoruz.
Olay Dinleyicileri (Event Listeners) ile Etkileşim
Web sayfalarını etkileşimli hale getiren temel mekanizma, kullanıcı eylemlerine (tıklamalar, klavye girişleri, fare hareketleri vb.) veya tarayıcı olaylarına (sayfa yüklenmesi, eleman boyutlandırma vb.) yanıt vermektir. Bu yanıtlar, olay dinleyicileri (event listeners) aracılığıyla sağlanır.
Olay Dinleyici Ekleme: addEventListener()
element.addEventListener(event, handler, options) yöntemi, belirli bir elemana belirli bir olay türü (event) gerçekleştiğinde çalışacak bir fonksiyonu (handler) atar. Bu, bir elemana birden fazla olay dinleyicisi eklemeye olanak tanır ve eski onclick gibi nitelik tabanlı yöntemlere göre daha esnektir.
* event: Dinlenecek olay türünü belirten bir dize (örneğin, ‘click’, ‘mouseover’, ‘submit’).
* handler: Olay gerçekleştiğinde çağrılacak fonksiyon. Bu fonksiyon, olay hakkında bilgi içeren bir Event nesnesi alır.
* options (isteğe bağlı): Olay dinleyicisinin davranışını yapılandıran bir nesne (örneğin, capture, once, passive).
const myButton = document.getElementById('myButton');
myButton.addEventListener('click', function(event) {
console.log('Düğmeye tıklandı!');
console.log(event.target); // Tıklanan elemanı döndürür (bu durumda myButton)
});
// Arrow fonksiyon ile:
myButton.addEventListener('mouseover', (event) => {
console.log('Fare düğmenin üzerine geldi.');
});
Yaygın Olay Türleri: click, mouseover, keydown, submit, load
JavaScript’te yüzlerce olay türü bulunur. En yaygın olanlardan bazıları:
* click: Bir elemana tıklandığında.
* dblclick: Bir elemana çift tıklandığında.
* mouseover: Fare imleci bir elemanın üzerine geldiğinde.
* mouseout: Fare imleci bir elemanın üzerinden ayrıldığında.
* mousedown / mouseup: Fare düğmesine basıldığında / bırakıldığında.
* keydown / keyup: Bir tuşa basıldığında / bırakıldığında (klavye olayları).
* input: Bir form elemanının değeri değiştiğinde (input, textarea, select).
* change: Bir form elemanının değeri değişip eleman odağı kaybettiğinde.
* submit: Bir form gönderildiğinde.
* focus / blur: Bir eleman odaklandığında / odağı kaybettiğinde.
* load: Sayfa veya eleman tamamen yüklendiğinde (örneğin, window.onload veya img.onload).
* DOMContentLoaded: HTML belgesi tamamen yüklendiğinde ve ayrıştırıldığında (resimler, stil dosyaları gibi dış kaynakların yüklenmesini beklemez).
Olay Nesnesi ve Özellikleri
Olay dinleyici fonksiyonuna otomatik olarak bir Event nesnesi geçirilir. Bu nesne, gerçekleşen olay hakkında detaylı bilgi içerir:
* event.target: Olayın orijinal olarak tetiklendiği eleman.
* event.currentTarget: Olay dinleyicisinin bağlı olduğu eleman.
* event.type: Olayın türü (örneğin, “click”).
* event.clientX / event.clientY: Fare olaylarında, farenin pencereye göre X/Y koordinatları.
* event.key / event.code: Klavye olaylarında, basılan tuşun değeri veya kodu.
* event.preventDefault(): Olayın varsayılan eylemini engeller (örneğin, bir formun gönderilmesini veya bir bağlantıya tıklamanın sayfayı yenilemesini engellemek).
* event.stopPropagation(): Olayın DOM ağacında daha fazla yayılmasını durdurur.
document.querySelector('a').addEventListener('click', function(event) {
event.preventDefault(); // Bağlantının varsayılan davranışını (sayfa yenileme) engeller
console.log('Bağlantıya tıklandı ama yönlendirme yapılmadı.');
});
Olay Dinleyiciyi Kaldırma: removeEventListener()
Bazen bir olay dinleyicisini kaldırmak gerekebilir (örneğin, bir kez çalıştıktan sonra veya belirli koşullar altında). removeEventListener() yöntemi, addEventListener() ile eklenmiş bir dinleyiciyi kaldırır. Dinleyicinin kaldırılabilmesi için, addEventListener()‘a geçirilen fonksiyon referansının aynı olması gerekir. Anonim fonksiyonlar kaldırılamaz.
function handleClick() {
console.log('Düğmeye tıklandı!');
}
myButton.addEventListener('click', handleClick); // Dinleyici ekle
// Daha sonra:
myButton.removeEventListener('click', handleClick); // Dinleyiciyi kaldır
Olay Yayılımı (Event Bubbling) ve Yakalama (Event Capturing)
Bir eleman üzerinde bir olay tetiklendiğinde, bu olay DOM ağacında yukarı veya aşağı doğru yayılır.
* Bubbling (Kabarcıklanma): Olay, tetiklendiği elemandan başlar ve ebeveynlerine doğru DOM ağacında yukarı doğru yayılır. Varsayılan davranıştır.
* Capturing (Yakalama): Olay, document nesnesinden başlar ve tetikleneceği elemana doğru DOM ağacında aşağı doğru yayılır. addEventListener‘ın üçüncü parametresi olarak { capture: true } geçirilerek etkinleştirilir.
// HTML:
const parent = document.getElementById('parent');
const child = document.getElementById('child');
parent.addEventListener('click', () => console.log('Parent yakaladı (kabarcıklanma)'), false); // Varsayılan: bubbling
child.addEventListener('click', () => console.log('Child yakaladı'), false);
// Çıktı (child'a tıklanınca):
// Child yakaladı
// Parent yakaladı (kabarcıklanma)
// Capturing örneği:
parent.addEventListener('click', () => console.log('Parent yakaladı (yakalama)'), { capture: true });
// Çıktı (child'a tıklanınca):
// Parent yakaladı (yakalama)
// Child yakaladı
// Parent yakaladı (kabarcıklanma)
Bu mekanizma, “olay delegasyonu” gibi güçlü desenler oluşturmak için kullanılır. Büyük listelerdeki her bir öğeye tek tek dinleyici eklemek yerine, listeye bir dinleyici ekleyip olay kabarcıklanma yoluyla geldiğinde event.target‘ı kontrol ederek hangi öğenin tıklandığını belirleyebilirsiniz.
event.preventDefault() ve event.stopPropagation()
* event.preventDefault(): Bir olayın varsayılan eylemini iptal eder. Örneğin, bir etiketine tıklandığında sayfa yenilemesini veya bir formun submit edildiğinde varsayılan olarak sunucuya gönderilmesini engellemek için kullanılır.
* event.stopPropagation(): Olayın DOM ağacında daha fazla yayılmasını (hem bubbling hem de capturing aşamalarında) durdurur. Bu, iç içe geçmiş elemanlarda gereksiz ebeveyn dinleyicilerinin tetiklenmesini önlemek için kullanışlıdır.
const link = document.getElementById('myLink');
link.addEventListener('click', function(event) {
event.preventDefault(); // Sayfa yönlendirmesini engeller
console.log('Bağlantıya tıklandı ama sayfa yenilenmedi.');
});
const outerDiv = document.getElementById('outer');
const innerDiv = document.getElementById('inner');
outerDiv.addEventListener('click', () => console.log('Dış div tıklandı.'));
innerDiv.addEventListener('click', function(event) {
event.stopPropagation(); // Olayın outerDiv'e yayılmasını engeller
console.log('İç div tıklandı.');
});
// innerDiv'e tıklanınca sadece "İç div tıklandı." çıktısı alınır.
Performans ve En İyi Uygulamalar
DOM manipülasyonu, web sayfalarını dinamik hale getirmenin temelidir, ancak yanlış kullanıldığında performans sorunlarına yol açabilir. Tarayıcılar, DOM’da yapılan her değişikliği yansıtmak için “reflow” (düzenin yeniden hesaplanması) ve “repaint” (elemanların yeniden çizilmesi) gibi maliyetli işlemler yapar. Bu işlemlerin sayısını minimize etmek, daha akıcı ve hızlı bir kullanıcı deneyimi sağlar.
DOM Manipülasyonunu Minimize Etme
* Toplu Değişiklikler Yapın: Mümkün olduğunca, birden fazla DOM değişikliğini tek bir işlemde birleştirin. Örneğin, bir döngü içinde her adımda DOM’a eleman eklemek yerine, tüm elemanları hafızada oluşturun ve ardından tek bir appendChild() veya append() işlemiyle DOM’a ekleyin.
* Elemanı DOM’dan Ayırın: Büyük değişiklikler yaparken, elemanı geçici olarak DOM’dan kaldırın, değişiklikleri yapın ve sonra tekrar DOM’a ekleyin. Bu, birden fazla reflow/repaint yerine tek bir reflow/repaint tetikler.
const list = document.getElementById('myList');
const parent = list.parentNode;
parent.removeChild(list); // Listeyi DOM'dan kaldır
// Burada list üzerinde birden fazla değişiklik yap
for (let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = Öğe ${i};
list.appendChild(li);
}
parent.appendChild(list); // Listeyi tekrar DOM'a ekle
Document Fragments Kullanımı
DocumentFragment nesnesi, DOM ağacının hafif bir versiyonudur. Gerçek DOM’a eklenmeden önce elemanları gruplandırmak ve üzerinde işlem yapmak için kullanılabilir. Bir DocumentFragment‘ı DOM’a eklediğinizde, fragment’ın kendisi değil, yalnızca çocukları eklenir. Bu, çok sayıda eleman eklerken performans avantajı sağlar çünkü sadece bir kez reflow/repaint tetiklenir.
const list = document.getElementById('myList');
const fragment = document.createDocumentFragment(); // Fragment oluştur
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.textContent = Öğe ${i};
fragment.appendChild(li); // Elemanları fragmente ekle
}
list.appendChild(fragment); // Fragment'ı tek seferde listeye ekle
Bu yöntem, özellikle büyük listeler veya tablolar oluştururken önemli performans kazançları sağlar.
Reflow ve Repaint Kavramları
* Reflow (Düzen Yeniden Hesaplama): Bir elemanın geometrik özelliklerini (boyut, konum) etkileyen bir değişiklik yapıldığında tetiklenir. Örneğin, width, height, padding, margin, display, position gibi CSS özelliklerinin değiştirilmesi reflow’a neden olur. Reflow, tüm etkilenen elemanların ve bazen tüm belgenin yeniden düzenlenmesini gerektirdiğinden oldukça maliyetlidir.
* Repaint (Yeniden Çizim): Bir elemanın görsel özelliklerini (renk, arka plan, görünürlük) etkileyen ancak geometrisini değiştirmeyen bir değişiklik yapıldığında tetiklenir. Örneğin, color, background-color, visibility gibi özelliklerin değiştirilmesi repaint’e neden olur. Repaint, reflow’dan daha az maliyetlidir ancak yine de performansı etkileyebilir.
Tarayıcılar, reflow ve repaint işlemlerini optimize etmek için genellikle değişiklikleri toplu olarak uygularlar, ancak JavaScript’ten elemanların boyut veya konum bilgilerini okumak (örneğin, offsetHeight, offsetWidth, getComputedStyle()) bu optimizasyonu bozar ve tarayıcıyı bekleyen tüm düzen değişikliklerini hemen uygulamaya zorlar. Bu nedenle, bu tür okuma işlemlerini minimize etmek önemlidir.
Asenkron İşlemler ve Debouncing/Throttling (Kısaca)
Yoğun olay dinleyicileri (örneğin, mousemove, scroll, resize, input) çok sık tetiklenebilir ve bu da DOM manipülasyonu yapılıyorsa performans sorunlarına yol açabilir.
* Debouncing: Bir fonksiyonun, belirli bir süre içinde tekrar tetiklenmediği sürece çalışmasını sağlar. Örneğin, bir arama kutusuna yazarken her tuş vuruşunda arama yapmak yerine, kullanıcı yazmayı bitirdikten kısa bir süre sonra arama yapmak için debouncing kullanılabilir.
* Throttling: Bir fonksiyonun, belirli bir zaman aralığında en fazla bir kez çalışmasını sağlar. Örneğin, bir kaydırma olayının her milisaniyede bir tetiklenmesini engellemek ve sadece her 100 milisaniyede bir tetiklenmesini sağlamak için throttling kullanılabilir.
Bu teknikler, yoğun DOM olaylarını yöneterek performansı artırır.
Modern Çerçevelerin (Frameworks) Rolü (Kısaca)
React, Vue, Angular gibi modern JavaScript çerçeveleri ve kütüphaneleri, DOM manipülasyonunu soyutlayarak geliştiricilerin doğrudan DOM ile uğraşma yükünü azaltır. Bu çerçeveler, genellikle bir “sanal DOM” (Virtual DOM) kullanarak veya optimize edilmiş diffing algoritmalarıyla gerçek DOM’daki değişiklikleri en verimli şekilde yaparlar. Bu sayede, geliştiriciler uygulamanın durumunu yönetmeye odaklanabilirken, performans optimizasyonları çerçeve tarafından halledilir. Ancak, bu çerçevelerin altında yatan prensipleri anlamak için temel DOM manipülasyon bilgisi yine de kritik öneme sahiptir.
Sonuç
DOM üzerinde değişiklik yapmak, modern ve etkileşimli web uygulamaları geliştirmenin temelidir. JavaScript’in sunduğu zengin DOM API’leri sayesinde, web sayfalarının içeriğini, yapısını ve stilini dinamik olarak değiştirebilir, kullanıcı etkileşimlerine yanıt verebilir ve zengin kullanıcı deneyimleri oluşturabiliriz.
Bu makalede, DOM elemanlarına çeşitli yöntemlerle nasıl erişileceğini (getElementById, querySelector, getElementsByClassName vb.), yeni elemanların nasıl oluşturulup (createElement, createTextNode) ve DOM’a nasıl ekleneceğini (appendChild, insertBefore, append), mevcut elemanların metin içeriğinin (textContent, innerHTML), niteliklerinin (setAttribute) ve stillerinin (style, classList) nasıl güncelleneceğini, ayrıca elemanların nasıl silineceğini (removeChild, remove) detaylı olarak inceledik.
Ek olarak, kullanıcı etkileşimlerini yönetmek için olay dinleyicilerinin (addEventListener) nasıl kullanılacağını, yaygın olay türlerini ve olay nesnesinin önemli özelliklerini ele aldık. Olay yayılımı, preventDefault() ve stopPropagation() gibi kavramlar, daha karmaşık etkileşimlerin yönetilmesinde kritik rol oynar.
Son olarak, DOM manipülasyonunun performans üzerindeki etkilerini göz önünde bulundurarak, değişiklikleri minimize etme, DocumentFragment kullanımı ve reflow/repaint gibi kavramlara değindik. Doğru teknikleri ve en iyi uygulamaları benimsemek, hem geliştirme sürecini kolaylaştıracak hem de son kullanıcıya daha akıcı ve performanslı bir deneyim sunacaktır. DOM’u ustaca kullanmak, bir web geliştiricisinin temel becerilerinden biridir ve bu yetkinlik, dinamik ve modern web dünyasında başarılı uygulamalar yaratmanın anahtarıdır.
Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Bir yanıt yazın Yanıtı iptal et
