DOM (Document Object Model), tarayıcının bir HTML veya XML belgesini bellekte nesnelerden oluşan bir ağaç olarak temsil etme biçimidir. Tarayıcı sayfayı ayrıştırırken bu ağacı kurar, sayfayı ekrana bu ağaca bakarak çizer ve JavaScript kodunuz sayfayı bu ağaç üzerinden okur ve değiştirir. Ağacın her dalı bir düğümle (node) biter; öğeler, metin parçaları ve yorumlar ayrı düğüm türleridir.
Bu yazıda DOM ağacının yapısını, düğüm türlerini, Node, Element ve Document arayüzleri arasındaki kalıtımı, window ile document farkını ve iki düğümü karşılaştırmanın yollarını MDN belgelerine dayanarak anlatıyoruz. Öğe seçme, oluşturma ve içerik değiştirme konuları serinin sonraki yazılarında; tüm sırayı Web API öğrenme rehberi sayfasında bulabilirsiniz.
DOM bir programlama dili mi?
Hayır. MDN'e göre DOM, web belgeleri için bir programlama arayüzüdür; belgeyi düğümler ve nesneler olarak temsil eder ki programlar belgenin yapısını, stilini ve içeriğini değiştirebilsin. DOM'u neredeyse her zaman JavaScript ile kullanırız, ama DOM dilden bağımsız tasarlanmıştır. MDN bunu göstermek için Python'un xml.dom.minidom modülüyle bir XML dosyasını ayrıştırıp getElementsByTagName() çağıran bir örnek verir: aynı arayüz adları başka bir dilde de vardır.
Tersi de geçerlidir: JavaScript DOM olmadan da çalışır. Node.js JavaScript programlarını bilgisayarda çalıştırır ama farklı bir API kümesi sunar; DOM, Node.js çalışma zamanının çekirdek bir parçası değildir. Bu yüzden document.querySelector() içeren bir kodu Node.js'te çalıştırdığınızda document tanımsız olur. Dilin kendisini öğrenmek için JavaScript öğrenme rehberi serisine bakabilirsiniz.
DOM tek bir API de değildir. Çekirdek DOM, her türlü belgeyi ve içindeki nesneleri tanımlayan temel varlıkları içerir. HTML DOM API bu çekirdeğe HTML belgelerine özgü özellikleri ekler; SVG API ise SVG belgeleri için aynısını yapar. Sekmeler, pencereler, CSS stilleri ve tarayıcı geçmişi gibi tarayıcı özelliklerine erişim de HTML DOM API tarafında tanımlanır.
DOM ağacı neye benzer?
MDN'in örnek belgesini ele alalım:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Document</title>
</head>
<body>
<h1>Hello, world!</h1>
<p>This is a paragraph.</p>
</body>
</html>Bu belgenin ağacının kökünde tüm belgeyi temsil eden bir Document düğümü vardır ve bu düğüm sayfada genel document değişkeniyle erişilebilir. Kökün iki önemli çocuğu bulunur: doctype bildirimini temsil eden bir DocumentType düğümü (document.doctype) ve kök öğe olan <html> (document.documentElement). <html> öğesinin altında <head> ve <body>, onların altında da diğer öğeler yer alır. <p> öğesinin tek çocuğu, "This is a paragraph." metnini taşıyan bir Text düğümüdür.
Gerçek ağaç, kaynaktaki boşlukları da korur. Etiketler arasındaki satır sonları ve girintiler de metin düğümü olarak ağaca girer. MDN bu yüzden çizimlerdeki ağacın gerçek DOM ağacına benzediğini ama onunla aynı olmadığını not eder. Bu ayrıntı, firstChild veya childNodes[0] ile beklediğiniz öğe yerine bir boşluk metin düğümü almanızın nedenidir; çözümü DOM ağacında gezinme yazısında anlattık.
Ağaç terimleri DOM belgelerinde sık geçer. Ebeveyni olmayan düğüm kök, çocuğu olmayan düğüm yaprak, aynı ebeveyni paylaşan düğümler kardeş olarak adlandırılır. Ebeveyn bağlantılarını izleyerek A'dan B'ye ulaşabiliyorsanız A, B'nin torunu (descendant), B de A'nın atası (ancestor) olur. Her düğümün tek bir kökü vardır ve ağaçta döngü bulunamaz: bir düğüm kendisinin atası veya torunu olamaz. Ağaç sırası (tree order veya document order) ise önce düğümün kendisinin, sonra çocuklarının sırayla ve özyinelemeli olarak listelendiği derinlik öncelikli sıradır. querySelectorAll() sonuçları da bu sırayla gelir.
Düğüm türleri ve nodeType değerleri

DOM'daki her düğüm Node arayüzünü uygular, ama pratikte hiçbir düğüm yalnızca Node değildir; her biri Node'dan türeyen daha özel bir arayüzün örneğidir. Düğümün türünü nodeType özelliği bir sayı olarak verir. MDN'in özet tablosu şöyledir:
| Arayüz | nodeType sabiti ve değeri | Geçerli çocuklar (yorum ve işleme talimatı dışında) |
|---|---|---|
| Document | Node.DOCUMENT_NODE (9) | DocumentType, Element |
| DocumentType | Node.DOCUMENT_TYPE_NODE (10) | Yok |
| Element | Node.ELEMENT_NODE (1) | Element, Text, CDATASection |
| Text | Node.TEXT_NODE (3) | Yok |
| CDATASection | Node.CDATA_SECTION_NODE (4) | Yok |
| Comment | Node.COMMENT_NODE (8) | Yok |
| ProcessingInstruction | Node.PROCESSING_INSTRUCTION_NODE (7) | Yok |
| Attr | Node.ATTRIBUTE_NODE (2) | Yok |
Tabloda olmayan 11 değeri (Node.DOCUMENT_FRAGMENT_NODE), belgeye bağlı olmadan düğüm toplamak için kullanılan DocumentFragment düğümüne aittir; onu öğe oluşturma yazısında ele aldık. 5, 6 ve 12 değerleri (ENTITY_REFERENCE_NODE, ENTITY_NODE, NOTATION_NODE) artık kullanılmaz. Çocuk alabilen düğümler (Document, DocumentFragment ve Element) ayrıca Comment ve ProcessingInstruction çocukları da alabilir; bunlar her zaman yapraktır. HTML belgeleri CDATA bölümlerine izin vermez; CDATA ancak SVG veya XML belgelerinde görülür.
Attr düğümleri ilginç bir istisnadır: Node arayüzünü genişletirler ama ana ağacın parçası değildirler. Hiçbir düğümün çocuğu olmadıkları için ebeveynleri null'dır; bunun yerine öğenin attributes özelliğindeki ayrı bir adlandırılmış düğüm haritasında (NamedNodeMap) tutulurlar. Öznitelikleri pratikte nasıl okuyup yazacağınızı classList, setAttribute ve style yazısında ele aldık.
const baslik = document.querySelector("h1");
console.log(baslik.nodeType === Node.ELEMENT_NODE); // öğe düğümü mü?
console.log(baslik.firstChild.nodeType === Node.TEXT_NODE); // ilk çocuk metin mi?
console.log(document.nodeType === Node.DOCUMENT_NODE);Sayı değerlerini ezberlemek yerine bu örnekteki gibi Node.ELEMENT_NODE türünden sabitlerle karşılaştırmak, kodu okunur kılar.
Node, Element ve HTMLElement: kalıtım zinciri

MDN, nesneler ile uyguladıkları arayüzler arasındaki ilişkinin kafa karıştırabileceğini kabul eder ve tablo öğesini örnek verir. Bir <table> nesnesi, createCaption() ve insertRow() gibi metotları içeren HTMLTableElement arayüzünü uygular. Aynı nesne bir HTML öğesi olduğu için HTMLElement ve Element arayüzlerini, DOM açısından ağaçtaki bir düğüm olduğu için de daha temel olan Node arayüzünü uygular. Node ise olay dinleyebilmesini sağlayan EventTarget'tan türer.
Pratikte bu, tek bir değişken üzerinden farklı katmanlardaki üyeleri fark etmeden kullandığınız anlamına gelir. MDN'in form örneği bunu iyi özetler: <form> öğesini temsil eden nesne name özelliğini HTMLFormElement arayüzünden, className özelliğini ise Element arayüzünden alır; ikisi de aynı nesnede durur.
| Katman | Ne sağlar | Örnek üyeler |
|---|---|---|
| EventTarget | Olay dinleme ve gönderme | addEventListener, removeEventListener, dispatchEvent |
| Node | Ağaç ilişkileri ve düğüm verisi | parentNode, childNodes, appendChild, textContent, nodeType |
| Element | Tüm öğelerde ortak olanlar | querySelector, getAttribute, classList, closest, id |
| HTMLElement | HTML öğelerine özgü olanlar | style, innerText, hidden, focus, click |
| HTMLTableElement gibi | Belirli bir etikete özgü olanlar | insertRow, createCaption, rows |
Kalıtımı bilmek MDN'de arama yaparken de işe yarar: disabled özelliğini Element sayfasında bulamazsınız, çünkü o HTMLButtonElement veya HTMLInputElement gibi daha özel arayüzlerde tanımlıdır. Tersine, appendChild()'ı HTMLDivElement sayfasında aramak yerine Node sayfasına bakmanız gerekir.
window ile document arasındaki fark
MDN'e göre DOM programlamada en sık kullandığınız iki nesne window ve document'tir. Basitçe söylemek gerekirse window tarayıcıyı, yani sayfanın içinde çalıştığı pencereyi ya da sekmeyi temsil eder; document ise belgenin kendisinin köküdür. window tarayıcıdaki genel nesnedir; document, localStorage, history ve fetch gibi pek çok API ona bağlı olarak erişilebilir. document ise ağacın kök düğümü olarak öğe seçme ve oluşturma metotlarını sunar.
Document düğümü kendi başına veri taşımaz: nodeValue ve textContent değerleri her zaman null, nodeName değeri her zaman "#document"'tır. Buna karşın belgeyle ilgili, çoğunlukla belgeyi sunan HTTP yanıtından gelen üst verileri verir:
document.URLvedocument.documentURI: belgenin adresi.document.characterSet: belgenin karakter kodlaması, örneğin"UTF-8".document.compatMode: çizim kipi; standart kip için"CSS1Compat", quirks kipi için"BackCompat".document.contentType: belgenin medya türü; HTML belgeleri için"text/html".document.doctype:DocumentTypedüğümü. HTML belgelerinde doctype her zaman<!doctype html>olduğundannamedeğeri"html",publicIdvesystemIdboş dizedir.document.documentElement: kök öğe; HTML belgelerinde genellikle<html>öğesidir.
Her düğüm hangi veriyi taşır?
Node arayüzü veriyle ilgili üç özellik tanımlar: nodeName, nodeValue ve textContent. Bu özelliklerin dönüş değeri düğüm türüne göre değişir:
| Düğüm türü | nodeName | nodeValue | textContent |
|---|---|---|---|
| Document | "#document" | null | null |
| DocumentType | doctype adı, örneğin "html" | null | null |
| Element | tagName, örneğin "P" veya "BODY" | null | tüm metin torunlarının ağaç sırasıyla birleşimi |
| Text | "#text" | data | data |
| Comment | "#comment" | data | data |
| Attr | özniteliğin adı | özniteliğin değeri | özniteliğin değeri |
Öğe düğümlerinde tagName HTML öğeleri için her zaman büyük harfle döner: <p class="note" id="intro"> için "P". MDN, bu değerin içeride yine küçük harfle saklandığını not eder. Bir öğenin textContent değeri, içindeki tüm metin düğümlerinin birleşimidir: <div>Hello, <span>world</span>!</div> için sonuç "Hello, world!" olur.
Text, Comment, CDATASection ve ProcessingInstruction düğümleri, Node'un bir alt sınıfı olan CharacterData arayüzünden türer. Bu arayüz düğümün metnini tutan data özelliğini, metnin uzunluğunu veren length özelliğini ve parça almak için substringData() metodunu tanımlar. Örneğin <!-- This is a comment --> yorum düğümünün data değeri baştaki ve sondaki boşluklar dahil " This is a comment " olur.
DOM'a nasıl erişirsiniz?
DOM'u kullanmak için özel bir şey yapmanız gerekmez. Sayfaya bağlı bir betik, ister satır içi <script> içinde ister ayrı bir dosyada olsun, document ve window nesnelerini doğrudan kullanabilir. MDN, sayfanın yapısını (HTML) ile DOM üzerinde işlem yapan JavaScript'i birbirine karıştırmamanızı önerir; <body onload="..."> gibi satır içi olay öznitelikleri yerine olay dinleyicilerini betikte eklemek daha düzenlidir.
MDN'in DOM sayfası, belgenin tamamını JavaScript ile kurmanın da mümkün olduğunu gösterir. Aşağıdaki kod, bu yazının başındaki belge ağacına benzer bir yapıyı sıfırdan oluşturur:
const root = document.createElement("html");
root.lang = "en";
const head = document.createElement("head");
const title = document.createElement("title");
title.appendChild(document.createTextNode("My Document"));
head.appendChild(title);
const body = document.createElement("body");
const header = document.createElement("h1");
header.appendChild(document.createTextNode("Header"));
const paragraph = document.createElement("p");
paragraph.appendChild(document.createTextNode("Paragraph"));
body.appendChild(header);
body.appendChild(paragraph);
root.appendChild(head);
root.appendChild(body);Kodda root henüz belgeye eklenmediği için bu ağaç bellekte durur ve ekranda görünmez. Bir düğümü sayfaya bağlamak, taşımak veya kaldırmak için kullanılan append(), before(), remove() gibi modern metotları DOM'da öğe oluşturma, ekleme ve silme yazısında ayrıntılı anlattık. Var olan bir öğeyi bulmanın yolları ise querySelector ve getElementById yazısında.
Mini örnek: düğme ile metni değiştirmek
MDN'in DOM sayfasındaki ilk örnek, üç temel parçayı bir araya getirir: querySelector() ile öğeyi bulmak, addEventListener() ile tıklamayı dinlemek ve textContent ile içeriği değiştirmek. HTML kısmı şöyle:
<div class="container">
<textarea class="story"></textarea>
<button id="set-text" type="button">Set text content</button>
<button id="clear-text" type="button">Clear text content</button>
</div>const story = document.querySelector(".story");
const setText = document.querySelector("#set-text");
setText.addEventListener("click", () => {
story.textContent = "It was a dark and stormy night...";
});
const clearText = document.querySelector("#clear-text");
clearText.addEventListener("click", () => {
story.textContent = "";
});İlk düğmeye tıklandığında metin alanına bir cümle yazılır, ikincisinde alan temizlenir. Bu üç adım (bul, dinle, değiştir) DOM programlamanın büyük kısmını oluşturur. Olay dinleme seçeneklerini addEventListener ile olay dinleme, içerik özelliklerinin farkını innerHTML, textContent ve innerText farkı yazısında bulabilirsiniz.
İki düğüm nasıl karşılaştırılır?

DOM, düğümleri karşılaştırmak için üç metot sunar ve her biri farklı bir soruya yanıt verir:
isSameNode(): MDN bu metodu eski (legacy) olarak tanımlar. Artık katı eşitlik operatörü===gibi davranır; yalnızca iki değişken aynı nesneyi gösteriyorsatruedöner. Doğrudana === byazmanız yeterlidir.isEqualNode(): Yapısal karşılaştırma yapar. İki düğüm aynı türdeyse, aynı veriyi taşıyorsa (öğeler için etiket adı ve öznitelikler, metin düğümleri içindata) ve her sıradaki çocukları da eşitsetruedöner. Aynı işaretlemeden üretilmiş iki ayrı öğe bu yüzden eşit sayılabilir.compareDocumentPosition(): İki düğümün ağaç sırasındaki göreli konumunu bir bit maskesi olarak döndürür.
a.compareDocumentPosition(b) sonucu şöyle yorumlanır: aynı düğümse 0; a, b'nin atasıysa DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_PRECEDING (10); a, b'nin torunuysa DOCUMENT_POSITION_CONTAINED_BY | DOCUMENT_POSITION_FOLLOWING (20); ağaç sırasında a önce geliyorsa DOCUMENT_POSITION_PRECEDING (2), sonra geliyorsa DOCUMENT_POSITION_FOLLOWING (4). İki düğüm aynı köke bağlı değilse dönen değer DISCONNECTED ve IMPLEMENTATION_SPECIFIC bitlerini içerir ve hangi yön bitinin ekleneceği tarayıcıya bağlıdır. Değer bit maskesi olduğu için kontrolü bit düzeyinde VE ile yaparsınız:
if (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_PRECEDING) {
// a precedes b
}Yalnızca bir düğümün diğerinin içinde olup olmadığını merak ediyorsanız contains() daha basittir; bu metodu ve ağaçta yukarı aşağı gezinmeyi DOM ağacında gezinme yazısında ele aldık.
DOM ile çalışırken sık yapılan hatalar
- Her düğümü öğe sanmak: MDN, DOM düğümlerine yaygın olarak "öğe" dendiğini ama her düğümün öğe olmadığını hatırlatır.
childNodesmetin ve yorum düğümlerini de içerir. - Boşluk metin düğümlerini unutmak: Kaynaktaki girintiler ve satır sonları da ağaca girer.
firstChildile beklediğiniz öğe yerine bir metin düğümü gelebilir; öğe istiyorsanızfirstElementChildkullanın. - HTML kaynağı ile DOM'u eş tutmak: JavaScript ile yaptığınız değişiklikler sunucudaki HTML dosyasını değil, bellekteki ağacı değiştirir. Sayfa yeniden yüklendiğinde ağaç, HTML kaynağından baştan kurulur.
- Betiği öğe oluşmadan çalıştırmak: Betik ilgili öğeden önce çalışırsa
querySelector()nulldöner. Sayfa yükleme sırasını DOMContentLoaded ve sayfa yaşam döngüsü yazısında anlattık. - DOM'u Node.js'te aramak:
documenttarayıcının sağladığı bir nesnedir; Node.js'te tanımlı değildir.
Sık Sorulan Sorular
DOM ile HTML arasındaki fark nedir?
HTML, sunucunun gönderdiği metindir. DOM ise tarayıcının bu metni ayrıştırarak bellekte kurduğu nesne ağacıdır. Aynı belgeyi hem tarayıcı penceresinde hem de HTML kaynağı olarak görebilirsiniz; DOM gösterimi, bu belgenin programla değiştirilebilmesini sağlar.
document.documentElement ile document.body farkı ne?
document.documentElement belgenin kök öğesidir ve HTML belgelerinde <html> öğesini verir. document.body ise bu kök öğenin içindeki <body> öğesidir. Belgenin tüm metnini almak için MDN document.documentElement.textContent kullanmayı önerir, çünkü document.textContent her zaman null'dır.
DOM'u Node.js'te kullanabilir miyim?
MDN'e göre DOM, Node.js çalışma zamanının çekirdek bir parçası değildir; Node.js farklı bir API kümesi sunar. Bu nedenle document ve Element gibi nesneler Node.js'te hazır gelmez. EventTarget, URL veya TextEncoder gibi ortamdan bağımsız standart arayüzler ise Node.js'te de bulunur.
DOM'daki eski arayüzler hâlâ kullanılabilir mi?
MDN, DOM'un zamanla büyük ölçüde sadeleştirildiğini ve DOM Level 3 veya öncesindeki DOMConfiguration, DOMUserData, ElementTraversal, Entity gibi bazı arayüzlerin kaldırıldığını belirtir. Bu arayüzler web geliştiricilerinin kullanımına artık açık değildir; eski bir örnekte bunlara rastlarsanız güncel karşılığını MDN'de arayın.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Web API öğrenme rehberi · querySelector ile öğe seçme · DOM öğe oluşturma · DOM ağacında gezinme · HTML öğrenme rehberi