DOM: डॉक्यूमेंट ऑब्जेक्ट मॉडल

Document Object Model (DOM) किसी web page का वह live model है जो browser memory में बनाता है। HTML को parse करते समय browser nodes का एक tree बनाता है — root पर document, हर element के लिए एक element node, शब्दों के लिए text nodes — और JavaScript को इस tree को पढ़ने और बदलने का interface देता है। Browser screen को DOM और CSS से बनाता है, HTML file से नहीं।

एक ठोस उदाहरण: cart page <input id="qty" value="1"> भेजता है। DOM में एक input element बनता है जिसका value attribute 1 है। जब user quantity को 3 कर देता है, तो element की value property 3 हो जाती है, attribute अब भी 1 रहता है, और page source बिल्कुल नहीं बदलता। Source वह page है जो भेजा गया था; DOM वह page है जो अभी है।

यह क्यों ज़रूरी है

UI test जो कुछ भी करता है, DOM के ज़रिए करता है। #qty जैसा locator मौजूदा tree पर एक query है, click किसी element node तक पहुँचाया जाता है, और assertion किसी property या node का text पढ़ता है। अगर आपको नहीं पता कि उस पल DOM में क्या था, तो आप तय नहीं कर सकते कि fail हुए test ने defect पकड़ा, गलत locator इस्तेमाल किया, या बहुत जल्दी देख लिया।

Manual testing में भी यह मायने रखता है। कुछ defects सिर्फ़ DOM में दिखते हैं: ऐसा message जो जुड़ता तो है पर hidden रहता है, ऐसा field जिसका label उससे जुड़ा नहीं, ऐसा button जो दो बार मौजूद है। DOM पढ़कर आप सिर्फ़ लक्षण नहीं, कारण report कर सकते हैं — element, उसके attributes और उसकी state — और जाँच सकते हैं कि समस्या page में है या आपके test में।

Nodes का एक tree

Browsers कैसे काम करते हैं, इस lesson में आपने देखा कि browser HTML को parse करता है। उस step का नतीजा DOM है: एक tree जिसकी हर चीज़ एक node है। Document node root है, जिसे scripts document नाम से इस्तेमाल करती हैं। हर HTML element एक element node बनता है, यानी एक branch जिसमें दूसरे nodes हो सकते हैं। Elements के अंदर का text, text nodes बनता है, जो tree के पत्ते (leaves) हैं। HTML comments, comment nodes बनते हैं: वे DOM में होते हैं, भले ही उन्हें कुछ भी दिखाता नहीं।

Attributes tree में children नहीं हैं। id="qty" input element पर ही रखा होता है, जैसे डिब्बे पर चिपका लेबल, न कि डिब्बे के अंदर रखी कोई चीज़। Tags के बीच की spaces और line breaks भी आम तौर पर text nodes बन जाती हैं, इसीलिए किसी element के child nodes गिनने वाली script को अक्सर tags से ज़्यादा nodes मिलते हैं।

  • Parent और child: नीचे के form में form उन label, input, button और p का parent है, और वे उसके children हैं।
  • Siblings: एक ही parent के children, जैसे label और input।
  • Ancestor और descendant: body, input का ancestor है; form के अंदर की हर चीज़ form की descendant है।
  • Locators भी यही रिश्ते इस्तेमाल करते हैं। CSS में form > input का मतलब है ऐसा input जो form का child हो, और form input यानी कोई भी descendant input। XPath में / child पर जाता है, // सारे descendants में खोजता है (expression की शुरुआत में पूरे document में), [@id='qty'] सिर्फ़ उन elements को रखता है जिनके attribute की यही value हो, और .. parent पर ऊपर जाता है: //input/.. input से उसके parent पर जाता है।
<form id="cart">
  <label for="qty">Quantity</label>
  <input id="qty" value="1">
  <button type="submit">Update</button>
  <p class="msg" hidden>Cart updated</p>
</form>
Browser में cart form इस तरह रहता है: element nodes document node से शाखाओं की तरह निकलते हैं, text nodes पत्ते हैं, और value="1" जैसे attributes अपने element पर रहते हैं, उसके अंदर नहीं।

DOM में cart form

भाग 1:#document
└ html
  ├ head
  └ body
    └ भाग 2:form id="cart"
      ├ label for="qty"
      │ └ भाग 4:"Quantity"
      ├ input भाग 3:id="qty" value="1"
      ├ button type="submit"
      │ └ भाग 4:"Update"
      └ p class="msg" भाग 5:hidden
        └ भाग 4:"Cart updated"
  1. 1Document nodeRoot, scripts में document: किसी element की हर खोज यहीं से या पहले मिले element से शुरू होती है
  2. 2Element nodeहर element के लिए एक, html से p तक। form चार children का parent है, जो आपस में siblings हैं
  3. 3AttributesElement पर रखे होते हैं, tree में children नहीं। User के quantity को 3 करने के बाद value property "3" है, जबकि यह attribute अब भी "1" कहता है
  4. 4Text nodesUser जो शब्द पढ़ता है; बिना children वाले पत्ते। Text locators इन्हीं से match करते हैं
  5. 5मौजूद, पर दिखता नहींhidden paragraph को screen से दूर रखता है, पर query उसे फिर भी पा लेती है: मौजूद होना दिखना नहीं है

head के children और tags के बीच के सिर्फ़ whitespace वाले text nodes छोड़ दिए गए हैं।

आज़माएँ: HTML फ़ॉर्मेटर

Attributes और properties

Attribute HTML में लिखा जाता है: value="1"। Property उस JavaScript object की होती है जो element को represent करता है: qty.value। बहुत से attributes के लिए browser दोनों को एक जैसा रखता है, जिसे HTML standard reflecting कहता है: id property बदलने से id attribute बदलता है, और उल्टा भी। कुछ जोड़ों के नाम अलग हैं: class attribute की property className है, और for की htmlFor।

Tester के लिए सबसे अहम वे जोड़े हैं जो एक जैसे नहीं रहते:

  • Input पर value: attribute शुरुआती value है, जिसे defaultValue property से भी पढ़ा जा सकता है। value property वह है जो field में अभी है। टाइप करने से सिर्फ़ property बदलती है।
  • Checkbox पर checked: attribute बताता है कि वह ticked होकर शुरू होता है या नहीं (defaultChecked); checked property बताती है कि वह अभी ticked है या नहीं।
  • Link पर href: getAttribute('href') वही लौटाता है जो HTML में लिखा है, जैसे /help; href property वह पूरा URL लौटाती है जो browser ने resolve किया, जैसे https://shop.example/help।
  • disabled और required जैसे boolean attributes: सिर्फ़ मौजूदगी मायने रखती है। disabled="false" भी button को disable करता है, क्योंकि attribute मौजूद है। hidden भी ऐसे ही काम करता है: किसी भी value के साथ, hidden="false" के साथ भी, element hidden रहता है।
// After the user changed the quantity from 1 to 3
const qty = document.querySelector('#qty');
qty.getAttribute('value');  // "1"  attribute: what the HTML said
qty.defaultValue;           // "1"  property that reflects the attribute
qty.value;                  // "3"  property: what the field holds now

JavaScript DOM को कैसे बदलती है

पिछले lesson की JavaScript page को DOM बदलकर बदलती है; फिर browser नए tree से screen दोबारा बनाता है। लगभग हर बदलाव कुछ ही operations में से एक होता है: node ढूँढना, नया node बनाना, उसे insert करना, हटाना, या उसका text, attributes या properties बदलना। Scripts आम तौर पर elements को document.querySelector() से ढूँढती हैं, जो CSS selector लेता है और पहला matching element लौटाता है, या कुछ भी match न हो तो null; querySelectorAll() सारे matches लौटाता है।

React, Angular और Vue जैसे frameworks आपकी जगह यही calls करते हैं। Data बदलने पर वे किसी element को हटाकर उसकी जगह नया element insert कर सकते हैं, इसलिए जो element आपके test ने एक पल पहले पाया था, हो सकता है वह अब tree में न हो। तब Selenium stale element reference error के साथ fail होता है; Playwright ज़्यादातर ऐसी failures से बचता है, क्योंकि locator के हर इस्तेमाल पर वह up-to-date element ढूँढता है।

  • Page source और DOM अलग हैं। View Page Source (Ctrl+U) वह दिखाता है जो server ने भेजा; DOM में उसके बाद scripts का जोड़ा हुआ सब कुछ भी होता है। Single-page application में source एक खाली <div id="root"></div> से ज़्यादा कुछ न भी हो, ऐसा हो सकता है।
  • Content page दिखने के बाद आ सकता है। जब कोई script data fetch करके insert करती है, तो शुरू में element मौजूद नहीं होता, इसलिए test को उसका इंतज़ार करना पड़ता है।
  • Parser भी tree बदलता है। <table> के अंदर सीधे लिखी table row, parser के insert किए <tbody> के अंदर पहुँच जाती है, इसलिए source से copy किया XPath, जैसे //table/tr, कुछ भी match नहीं करता।
  • innerHTML से insert किया text markup की तरह parse होता है, जबकि textContent उसे सादा text बनाकर डालता है। अगर <b>Ali</b> लिखा गया नाम कहीं bold दिखे, तो user input DOM में markup बनकर पहुँच रहा है: इसे report करें, क्योंकि यही रास्ता cross-site scripting (XSS) की गुंजाइश दे सकता है।
const form = document.querySelector('#cart');   // find an element
const msg = form.querySelector('.msg');
msg.textContent = 'Cart updated: 3 items';     // replace its text
msg.hidden = false;                            // removes the hidden attribute
const note = document.createElement('p');      // create a node, not in the tree yet
note.textContent = 'Free delivery';
form.append(note);                             // insert it: now it can be seen
note.remove();                                 // remove it again

DOM को inspect करना, और locators इस पर क्यों निर्भर हैं

किसी element पर right-click करके Inspect चुनें: developer tools Elements panel (Firefox में Inspector) पर खुलते हैं और वह element selected होता है। यह panel source नहीं, live DOM दिखाता है: scripts के जोड़े elements इसमें दिखते हैं, हटाए गए गायब हो जाते हैं, और attributes बदलते ही update होते हैं। इसका tree attributes दिखाता है, properties नहीं, इसलिए quantity को 3 करने के बाद भी यह value="1" दिखाता है। Property पढ़ने के लिए Console में $0.value टाइप करें; $0 वह element है जो Elements panel में selected है।

Page source में scripts का जोड़ा content और parser का insert किया tbody नहीं दिखता; Elements tree दोनों दिखाता है पर value attribute दिखाता है, इसलिए तीनों में से सिर्फ़ Console ($0.value) पढ़ता है कि user ने क्या टाइप किया।

View Page SourceCtrl+U

HTML, जैसा server ने भेजा

Scripts के जोड़े elements:
नहीं
Parser का insert किया tbody:
नहीं
User ने field में क्या टाइप किया:
नहीं
इसका इस्तेमाल:
देखना कि server ने क्या भेजा

Elements panel

Live DOM: nodes और उनके attributes, इस समय

Scripts के जोड़े elements:
हाँ
Parser का insert किया tbody:
हाँ
User ने field में क्या टाइप किया:
नहीं: यह value attribute दिखाता है
इसका इस्तेमाल:
Locators लिखना और debug करना

Console$0

Elements panel में selected element की कोई भी property

Scripts के जोड़े elements:
हाँ
Parser का insert किया tbody:
हाँ
User ने field में क्या टाइप किया:
हाँ: $0.value
इसका इस्तेमाल:
Properties पढ़ना और locator के matches गिनना

हर locator इसी live tree पर एक query है। CSS selectors और XPath element के नाम, attributes और positions match करते हैं; text locators nodes का text match करते हैं; Playwright के getByRole जैसे role-based locators element, उसके attributes, text और उसके <label> जैसे जुड़े elements से निकाले गए role और accessible name का इस्तेमाल करते हैं। इसीलिए locators उस state के DOM पर लिखें और debug करें जो test को मिलेगी, page source पर कभी नहीं।

  • Locator पर भरोसा करने से पहले matches गिनें: Console में document.querySelectorAll('#cart button').length को 1 लौटाना चाहिए। Chrome और Edge में Elements panel का Ctrl+F भी CSS selector या XPath expression स्वीकार करता है।
  • सही state में जाँचें: data load होने के बाद, dialog खुला होने पर, और हर उस भाषा में जिसमें suite चलता है।
  • मौजूद होना दिखना नहीं है। hidden वाला p DOM में है, इसलिए query उसे पा लेती है, पर user उसे नहीं देख सकता। जब requirement यह हो कि user कुछ देखे, तो visibility assert करें।
  • Iframe में एक अलग document होता है, और shadow root किसी component के अंदरूनी tree को आम queries से छिपाता है। दोनों को खास handling चाहिए, जो locator strategies guide में बताई गई है।

Worked example: cart page पर तीन checks

एक tester इस lesson का cart form और साथ में order history table automate करता है। Requirement: user के quantity बदलकर Update click करने के बाद "Cart updated" message दिखना चाहिए। तीन checks चौंकाने वाले नतीजे देते हैं, और developer tools में हर check दोहराने पर दिखता है कि DOM उसे कैसे समझाता है।

  • Check 1 fail होता है: quantity को 3 करने के बाद test input का value attribute पढ़ता है और उसे "1" मिलता है। Elements panel में input select करके इसे Console में दोहराएँ: $0.getAttribute('value') "1" देता है, जबकि $0.value "3" देता है। Application सही है; test ने property की जगह attribute पढ़ा। Fix: check को value property पढ़ने वाला बनाएँ। Automation tools भी यही attribute और property का जोड़ा अपने नामों से देते हैं, और कौन-सी call किसे पढ़ती है, यह Playwright और Selenium lessons में बताया गया है।
  • Check 2 fail होता है: page source से copy किया //table[@id='orders']/tr कोई row नहीं पाता। Console में $x() किसी XPath expression को चलाकर matching elements लौटाता है: $x("//table[@id='orders']/tr") खाली list [] लौटाता है, और Elements panel parser का insert किया tbody दिखाता है। Fix: rows को वैसे locate करें जैसी वे DOM में हैं: $x("//table[@id='orders']/tbody/tr") हर row लौटाता है।
  • Check 3 pass होता है, पर नहीं होना चाहिए था: test .msg का textContent पढ़ता है और उसे "Cart updated: 3 items" मिलता है। Run देख रहे tester को कोई message नहीं दिखता। Elements panel में p पर अब भी hidden है, और उसे select करके Console में $0.hidden true लौटाता है: script ने text बदला पर उसे कभी दिखाया नहीं। यह असली defect है, और test इसे चूक गया क्योंकि उसने visibility नहीं, text जाँचा।
  • Script ने ठीक-ठीक क्या बदला, यह देखने के लिए Update click करने से पहले और बाद में form का outerHTML copy करें (Elements panel में element पर right-click करके Copy, फिर Copy outerHTML चुनें) और दोनों copies को Text Diff में compare करें। Elements panel की तरह outerHTML भी attributes दिखाता है, टाइप की गई values नहीं।

आज़माएँ: टेक्स्ट डिफ़

आम गलतियाँ

  • View Page Source को DOM मान लेना, और उसी से locators या bug reports लिखना।
  • User ने क्या टाइप किया, यह value attribute या Elements panel से जाँचना; current value value property है।
  • मान लेना कि query से मिला element user को दिख रहा है; मौजूदगी और visibility अलग checks हैं।
  • disabled="false" या hidden="false" को enabled या visible समझना; इन attributes में value चाहे जो हो, मौजूदगी ही मायने रखती है।
  • Re-render के पार किसी element का reference रखकर बाद में इस्तेमाल करना, जिसका अंत stale element error में होता है।
  • "Copy XPath" या source से लिया locator, live DOM में यह जाँचे बिना इस्तेमाल करना कि वह ठीक एक element से match करता है।

QA का नज़रिया

Team में DOM testers और developers के बीच साझा ज़मीन है। UI defect report करते समय वह जोड़ें जो DOM ने दिखाया: element (छोटा selector या उसका outerHTML), ज़रूरी attribute या property values, और यह कि element गायब था, hidden था या दो बार मौजूद था। "Update के बाद p.msg पर hidden attribute बना रहता है" मिनटों में ठीक हो सकता है; "message नहीं दिखता" developer को खोज में भेज देता है।

Automate करते समय developers से जल्दी stable hooks माँगें — सार्थक ids, accessible names या तय किया हुआ test id attribute — और पूछें कि data बदलने पर page के कौन-से हिस्से re-render होते हैं। किसी fail हुए UI test को product defect के रूप में report करने से पहले, उस state का DOM देखें जिसमें वह fail हुआ: कई failures test की समस्याएँ होती हैं, जैसे stale element, source से copy किया locator या content आने से पहले हुआ check, और DOM बताता है कि कौन-सी।

अभ्यास

Sign-up form का DOM पढ़ें

आपका काम

नीचे newsletter sign-up page का एक हिस्सा है। Requirement: terms स्वीकार होने तक Join button disabled रहता है, और उस पर click के बाद "Check your inbox" message दिखता है। User ana@example.com टाइप करता है और checkbox tick करता है। आखिर की script हर बार checkbox बदलने पर चलती है और button की disabled property को checkbox की checked property का उल्टा कर देती है (! का मतलब है "नहीं")।

पहले steps के जवाब कागज़ पर लिखें। फिर अपने जवाब जाँचें: code को .html file के रूप में save करें, उसे browser में खोलें और Elements panel व Console इस्तेमाल करें।

सामग्री
<section id="signup">
  <h2>Newsletter</h2>
  <!-- shown to visitors who are not signed in -->
  <label for="email">Email</label>
  <input id="email" type="email" value="">
  <label><input id="terms" type="checkbox"> I accept the terms</label>
  <button id="join" disabled>Join</button>
  <p id="status" hidden></p>
</section>
<script>
  const terms = document.querySelector('#terms');
  const join = document.querySelector('#join');
  terms.addEventListener('change', () => { join.disabled = !terms.checked; });
</script>
चरण
  1. section का DOM tree बनाएँ: element nodes, text nodes और comment। सिर्फ़ whitespace वाले text nodes छोड़ दें।
  2. User के actions के बाद email field का value attribute और value property, और checkbox का checked attribute और checked property लिखें।
  3. Box tick होने पर button के disabled attribute का क्या होता है, और Elements panel क्या दिखाता है?
  4. इनमें से कौन-से locators ठीक एक element से match करते हैं: #signup label, #signup input, #terms, label[for=email]?
  5. एक साथी का test Join click करने के बाद #status पाता है और pass हो जाता है क्योंकि element मौजूद है। इस check में क्या गलत है, और बेहतर check क्या पाएगा?
मॉडल उत्तर दिखाएँ
मॉडल उत्तर

Tree: section#signup के क्रम से छह element children हैं — h2, label, input#email, label, button#join, p#status — और h2 व पहले label के बीच comment node है। h2 में text node "Newsletter" है; पहले label में "Email"; दूसरे label के दो children हैं, checkbox input#terms और text node " I accept the terms"; button में "Join"; p#status का कोई child नहीं। for, type, disabled और hidden जैसे attributes अपने elements पर रहते हैं, tree में नहीं।

Email field: value attribute अब भी खाली string है, जैसा लिखा था; value property "ana@example.com" है। Checkbox: कोई checked attribute नहीं है (getAttribute('checked') null लौटाता है), जबकि checked property true है।

Script disabled property को false करती है; disabled reflected है, इसलिए browser attribute हटा देता है और Elements panel <button id="join">Join</button> दिखाता है। Box untick करने पर disabled वापस आ जाता है।

#terms और label[for=email] एक-एक element से match करते हैं। #signup label दो labels से match करता है, और #signup input दो inputs से: email field और checkbox।

Paragraph शुरू से DOM में है, hidden और खाली, इसलिए "element मौजूद है" click से पहले और बाद दोनों में सच है और कुछ साबित नहीं करता। बेहतर check assert करता है कि #status visible है और उसमें "Check your inbox" है। इस page पर वह fail होगा: कोई script message को न भरती है न दिखाती है, और यह defect report करने लायक है, element और उसके hidden attribute को सबूत के रूप में देकर।

अक्सर पूछे जाने वाले सवाल

क्या DOM और HTML एक ही हैं?

नहीं। HTML वह text है जो server भेजता है; DOM वह tree है जो browser उससे बनाता है और बदलता रहता है। Parser markup को पूरा और ठीक करता है, और scripts nodes जोड़ती, हटाती और बदलती हैं, इसलिए page load होने के बाद दोनों में काफ़ी फ़र्क हो सकता है। View Page Source HTML दिखाता है; Elements panel DOM दिखाता है।

क्या tester को DOM API सीखनी चाहिए?

आपको DOM पढ़ना आना चाहिए, उसकी programming नहीं: node types और उनके रिश्ते, attributes और properties का फ़र्क, और value या locator जाँचने के लिए Console के कुछ commands, जैसे $0 और document.querySelectorAll। Automation API का ज़्यादा हिस्सा इस्तेमाल करता है, पर उसके locators और assertions इन्हीं ideas पर टिके हैं।

मेरा test वह element क्यों नहीं ढूँढ पाता जो मुझे screen पर दिखता है?

आम तौर पर इसलिए कि element iframe के अंदर है, जो अलग document है; या shadow root के अंदर है; या test के देखने तक जुड़ा ही नहीं था; या locator live DOM की बजाय page source से लिखा गया था। Screen पर दिखने वाली कुछ चीज़ें elements होती ही नहीं: जैसे canvas पर बना text, जिसे कोई DOM locator नहीं ढूँढ सकता।

संदर्भ

  • HTML Living StandardWHATWGHTML भाषा, नामित कैरेक्टर रेफ़रेंस (एंटिटी) और ब्राउज़रों के पार्सिंग नियमों सहित।
  • WebDriver (W3C)W3C · 2026वह W3C प्रोटोकॉल जिसे ब्राउज़र Selenium जैसे ऑटोमेशन टूल से रिमोट कंट्रोल के लिए लागू करते हैं (Level 2 संस्करण Working Draft है)।
  • Document Object Model (DOM) — MDNMDN Web Docs (Mozilla)DOM: नोड्स का वह ट्री जो ब्राउज़र पेज से बनाता है, और जिसे स्क्रिप्ट — और UI टेस्ट टूल — पढ़ते और बदलते हैं।
  • Locators — PlaywrightMicrosoft (Playwright)Playwright एलिमेंट कैसे ढूँढता है, locator दोबारा resolve होकर अपने-आप इंतज़ार क्यों करते हैं, और role-आधारित locator पहले क्यों सुझाए जाते हैं।

Mutqan tools से अभ्यास करें

नेस्टेड एलिमेंट को सही इंडेंटेशन देकर HTML सुंदर बनाएँ, pre, script और style ब्लॉक सुरक्षित रखते हुए।

फ़ॉर्मेटिंग

दो टेक्स्ट की लाइन-दर-लाइन या शब्द-दर-शब्द तुलना करें और अंतर हाइलाइट करें।

फ़ॉर्मेटिंग

और गहराई से

इस topic में आपकी प्रगति

QA रोडमैप का हिस्सा