दिन 12: Elements बनाना और Data से Lists Render
✅ आज आप सीखेंगे
- createElement, append, prepend, before, after, remove
- Objects के array से product grid render करना
- Template literals बनाम createElement बनाम <template>
- तेज़ batch inserts के लिए DocumentFragment
- State → render() → UI: वह pattern जिस पर React बना है
🏢 असली दुनिया में
Menu रोज़ बदलता है: नई मिठाइयाँ, sold-out items, price बदलाव। हर सुबह हाथ से HTML बदलना असंभव है। असली websites products को data (database या API से) के रूप में रखती हैं और JavaScript से HTML बनाती हैं। आज आप array से Sharma Sweets का product grid बनाएँगे – कल का API data भी यही तकनीक इस्तेमाल करेगा।
Elements बनाना और डालना
<ul id="list"><li>Kaju Katli</li></ul>
<button id="add">append</button> <button id="first">prepend</button> <button id="del">remove last</button>
<script>
const list = document.querySelector('#list');
document.querySelector('#add').addEventListener('click', () => {
const li = document.createElement('li'); // 1. create
li.textContent = 'Ghewar ' + (list.children.length + 1); // 2. fill (safe text)
li.className = 'new';
list.append(li); // 3. insert at the end
});
document.querySelector('#first').addEventListener('click', () => {
const li = document.createElement('li');
li.textContent = '⭐ Today’s special';
list.prepend(li); // insert at the start
});
document.querySelector('#del').addEventListener('click', () => {
list.lastElementChild?.remove(); // remove (?. = only if it exists)
});
</script>| Method | Node कहाँ जाता है… |
|---|---|
| parent.append(node, "text") | Parent के अंदर अंत में (एक साथ कई) |
| parent.prepend(node) | Parent के अंदर शुरू में |
| el.before(node) / el.after(node) | el के ठीक बाहर, पहले या बाद |
| el.replaceWith(node) | el की जगह |
| el.remove() | el हटाता है |
| parent.replaceChildren(...nodes) | Parent साफ़ करके नए nodes डालता है – re-render के लिए perfect |
Data से product grid render करना
<style>
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; }
.card { border: 1px solid #e5e7eb; border-radius: 12px; overflow: hidden; background: #fff; }
.card img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }
.card div { padding: 8px 10px; }
.card button { width: 100%; margin-top: 6px; }
.card.out { opacity: .55; }
</style>
<div class="grid" id="grid"></div>
<script>
const products = [
{ id: 1, name: 'Kaju Katli', price: 900, image: 'images/kaju-katli-400.webp', stock: 25 },
{ id: 2, name: 'Malai Ghewar', price: 650, image: 'images/ghewar-400.webp', stock: 8 },
{ id: 3, name: 'Rasmalai', price: 700, image: 'images/rasmalai-400.webp', stock: 0 },
{ id: 4, name: 'Besan Ladoo', price: 500, image: 'images/ladoo-400.webp', stock: 40 },
];
function createCard(p) {
const card = document.createElement('article');
card.className = 'card' + (p.stock === 0 ? ' out' : '');
const img = document.createElement('img');
img.src = p.image;
img.alt = p.name;
img.loading = 'lazy';
const body = document.createElement('div');
const title = document.createElement('b');
title.textContent = p.name; // text is always safe
const price = document.createElement('div');
price.textContent = `₹${p.price}/kg`;
const btn = document.createElement('button');
btn.textContent = p.stock === 0 ? 'Sold out' : 'Add to cart';
btn.disabled = p.stock === 0;
btn.dataset.id = p.id;
body.append(title, price, btn);
card.append(img, body);
return card;
}
document.querySelector('#grid').replaceChildren(...products.map(createCard));
</script>Data से HTML बनाने के तीन तरीक़े
| तकनीक | फ़ायदे | नुक़सान |
|---|---|---|
| Template literal + innerHTML | छोटा और पठनीय | User data के साथ XSS ख़तरा; अंदर के listeners मिटते हैं |
| createElement ⭐ | सुरक्षित (textContent), सटीक | ज़्यादा lines |
| <template> + cloneNode | HTML, HTML में रहता है, सुरक्षित भरना | Page में template चाहिए |
| Frameworks (React, Vue) | अपने-आप कुशल updates | Vanilla JS के बाद सीखें |
<template id="row">
<tr><td class="name"></td><td class="qty"></td><td class="amount"></td></tr>
</template>
<table border="1" cellpadding="6" style="border-collapse:collapse">
<thead><tr><th>Item</th><th>Qty</th><th>Amount</th></tr></thead>
<tbody id="body"></tbody>
</table>
<script>
const lines = [
{ name: 'Kaju Katli', qty: 2, price: 900 },
{ name: 'Rasmalai', qty: 1, price: 700 },
];
const tpl = document.querySelector('#row');
const fragment = document.createDocumentFragment(); // build off-screen
for (const line of lines) {
const row = tpl.content.cloneNode(true);
row.querySelector('.name').textContent = line.name;
row.querySelector('.qty').textContent = line.qty + ' kg';
row.querySelector('.amount').textContent = '₹' + line.qty * line.price;
fragment.append(row);
}
document.querySelector('#body').append(fragment); // ONE insert into the page
</script>State → render() → UI
<style>
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; }
.card { border: 1px solid #e5e7eb; border-radius: 12px; overflow: hidden; background: #fff; }
.card img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }
.card div { padding: 8px 10px; }
.card button { width: 100%; margin-top: 6px; }
.card.out { opacity: .55; }
</style>
<div class="grid" id="grid"></div>
<p id="cart"><b>Cart:</b> empty</p>
<script>
const products = [
{ id: 1, name: 'Kaju Katli', price: 900, image: 'images/kaju-katli-400.webp', stock: 25 },
{ id: 2, name: 'Malai Ghewar', price: 650, image: 'images/ghewar-400.webp', stock: 8 },
{ id: 3, name: 'Rasmalai', price: 700, image: 'images/rasmalai-400.webp', stock: 0 },
{ id: 4, name: 'Besan Ladoo', price: 500, image: 'images/ladoo-400.webp', stock: 40 },
];
const state = { cart: {} }; // id → qty
function render() {
const grid = document.querySelector('#grid');
grid.innerHTML = '';
for (const p of products) {
const qty = state.cart[p.id] || 0;
const card = document.createElement('article');
card.className = 'card' + (p.stock === 0 ? ' out' : '');
card.innerHTML = `<img src="${p.image}" alt=""><div><b></b><div>₹${p.price}/kg</div>
<button data-id="${p.id}" ${p.stock === 0 ? 'disabled' : ''}>${qty ? 'Add more (' + qty + ')' : 'Add to cart'}</button></div>`;
card.querySelector('b').textContent = p.name;
grid.append(card);
}
const lines = products.filter(p => state.cart[p.id]).map(p => `${p.name} × ${state.cart[p.id]}`);
const total = products.reduce((s, p) => s + (state.cart[p.id] || 0) * p.price, 0);
document.querySelector('#cart').innerHTML = '<b>Cart:</b> ' + (lines.length ? lines.join(', ') + ` = ₹${total}` : 'empty');
}
document.querySelector('#grid').addEventListener('click', e => { // delegation (Day 11)
const btn = e.target.closest('button[data-id]');
if (!btn) return;
const id = Number(btn.dataset.id);
state.cart[id] = (state.cart[id] || 0) + 1; // 1. update state
render(); // 2. re-render
});
render();
</script>React में आप setCart(...) लिखते हैं और React आपका component (render) ख़ुद बुलाकर सिर्फ़ बदला हुआ हिस्सा update करता है। यह vanilla version समझने से React स्वाभाविक लगता है।
🏅 आज के Job Skills
Practice tasks
🏋️ Practice – ख़ुद करें
Solution देखें
Quick quiz
🧠 Quick Quiz – ख़ुद को जाँचें
Interview corner
💼 Interview Corner
DOM में 1,000 items कुशलता से कैसे जोड़ें?
DocumentFragment में बनाएँ (या escaped data वाली एक HTML string) और एक बार डालें, loop में layout properties न पढ़ें, और बहुत लंबी lists के लिए virtualisation (सिर्फ़ दिखती rows render) सोचें।
Frameworks manual DOM edits की जगह render function क्यों लेते हैं?
State से UI एक जगह बनाना उसे एक-सा और अनुमानित रखता है; framework नतीजे का diff करके न्यूनतम DOM बदलाव करता है, जिससे page के कुछ हिस्सों के update भूलने वाले bugs नहीं होते।
सारांश
- createElement → textContent से भरें → append/prepend; हटाने को remove()।
- map/forEach और replaceChildren से arrays की lists render।
- साफ़, तेज़ batches के लिए <template> + DocumentFragment।
- State → render() → UI; events state बदलते हैं, DOM सीधे नहीं।
Day 13: forms – FormData से values पढ़ें, professional की तरह validate करें और उपयोगी error messages दिखाएँ।
