दिन 22: काम के Browser APIs
✅ आज आप सीखेंगे
- Share होने वाले filters के लिए URL और URLSearchParams
- History API: pushState, popstate
- IntersectionObserver: lazy loading, infinite scroll, reveal animations
- Clipboard, Web Share और WhatsApp deep links
- Geolocation और permissions – ज़िम्मेदारी से
🏢 असली दुनिया में
दुकान मालिक पूछते हैं: “जब मैं customer को ₹700 से कम milk sweets का link भेजूँ, तो वह filter पहले से लगा खुले।” “क्या customers एक tap में coupon copy और product WhatsApp पर share कर सकते हैं?” “क्या पता चले customer दुकान से कितना दूर है?” यह सब browser में built-in है – कोई libraries नहीं चाहिए।
URL और URLSearchParams
const link = new URL('https://sharmasweets.in/menu?category=milk&max=700&sort=price#list');
console.log(link.hostname, link.pathname, link.hash);
console.log(link.searchParams.get('category'), Number(link.searchParams.get('max')));
console.log(link.searchParams.has('veg'));
link.searchParams.set('max', 900); // update
link.searchParams.append('tag', 'sugar-free');
link.searchParams.delete('sort');
console.log(link.toString());
const p = new URLSearchParams({ q: 'kaju katli', city: 'Jaipur' });
console.log(p.toString()); // spaces and special characters encoded safelyHistory API से share होने वाले filters
<label>Category <select id="cat"><option value="">All</option><option value="milk">Milk</option><option value="dry">Dry</option></select></label>
<label>Max ₹ <input id="max" type="number" step="100" value="1000" style="width:90px"></label>
<p>Address bar would show: <code id="addr"></code></p>
<ul id="list"></ul>
<script>
const products = [
{ name: 'Kaju Katli', category: 'dry', price: 900 }, { name: 'Ghewar', category: 'milk', price: 650 },
{ name: 'Rasmalai', category: 'milk', price: 700 }, { name: 'Ladoo', category: 'dry', price: 500 },
];
const cat = document.querySelector('#cat'), max = document.querySelector('#max');
function render() {
const list = products.filter(p => (!cat.value || p.category === cat.value) && p.price <= Number(max.value));
document.querySelector('#list').innerHTML = list.map(p => `<li>${p.name} – ₹${p.price}</li>`).join('') || '<li>None</li>';
}
function updateUrl() {
const params = new URLSearchParams();
if (cat.value) params.set('category', cat.value);
if (max.value !== '1000') params.set('max', max.value);
const url = location.pathname + (params.toString() ? '?' + params : '');
try { history.pushState({ category: cat.value, max: max.value }, '', url); } catch (e) { /* sandboxed preview */ }
document.querySelector('#addr').textContent = '/menu' + (params.toString() ? '?' + params : '');
}
// read filters from the URL on load (works on a real page)
const start = new URLSearchParams(location.search);
if (start.get('category')) cat.value = start.get('category');
if (start.get('max')) max.value = start.get('max');
[cat, max].forEach(el => el.addEventListener('input', () => { updateUrl(); render(); }));
window.addEventListener('popstate', (e) => { // Back / Forward buttons
cat.value = e.state?.category ?? ''; max.value = e.state?.max ?? '1000'; render();
});
updateUrl(); render();
</script>IntersectionObserver: चीज़ें दिखने पर प्रतिक्रिया
<style>
.reveal { padding: 18px; margin: 8px 0; background: #fef3c7; border-radius: 10px; opacity: 0; transform: translateY(16px); transition: .5s; }
.reveal.show { opacity: 1; transform: none; }
</style>
<p>Scroll inside this box ↓</p>
<div id="feed" style="height:260px;overflow:auto;border:1px solid #e5e7eb;border-radius:10px;padding:10px"></div>
<script>
const feed = document.querySelector('#feed');
let page = 0;
const names = ['Kaju Katli', 'Ghewar', 'Rasmalai', 'Ladoo', 'Kalakand', 'Soan Papdi', 'Peda', 'Jalebi'];
const sentinel = document.createElement('p'); // invisible marker at the bottom
sentinel.textContent = 'Loading more…';
function addPage() {
page++;
for (let i = 0; i < 4; i++) {
const card = document.createElement('div');
card.className = 'reveal';
card.textContent = `#${(page - 1) * 4 + i + 1} ${names[((page - 1) * 4 + i) % names.length]}`;
feed.insertBefore(card, sentinel);
revealObserver.observe(card);
}
if (page >= 5) { sentinel.textContent = 'That’s all 🍬'; moreObserver.disconnect(); }
}
// 1. Reveal animation when a card enters the box
const revealObserver = new IntersectionObserver((entries) => {
entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('show'); revealObserver.unobserve(e.target); } });
}, { root: feed, threshold: 0.3 });
// 2. Infinite scroll: load the next page when the sentinel appears
const moreObserver = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) addPage();
}, { root: feed, rootMargin: '80px' });
feed.append(sentinel);
moreObserver.observe(sentinel);
</script>| उपयोग | कैसे |
|---|---|
| Images lazy-load | img[data-src] observe करें; दिखने पर src लगाएँ (या बस loading="lazy") |
| Infinite scroll | List के अंत में sentinel observe करके अगला page fetch |
| Scroll reveal animations | Viewport में आने पर class जोड़ें |
| Analytics “seen” events | Banner 1 s तक 50% दिखे तो viewed गिनें |
| Videos रोकना | Screen से बाहर जाने पर pause |
scroll हर सेकंड दर्जनों बार चलता है और positions मापने से layout गणना होती है – सस्ते phones पर झटके। IntersectionObserver browser कुशलता से गिनता है और तभी बुलाता है जब दिखना सच में बदले।
Clipboard, Web Share और WhatsApp links
<p>Coupon: <code id="code">SWEET10</code> <button id="copy">Copy</button></p>
<p><button id="share">Share this product</button>
<a id="wa" target="_blank" rel="noopener">Order on WhatsApp</a></p>
<p id="msg" role="status"></p>
<script>
const msg = document.querySelector('#msg');
document.querySelector('#copy').addEventListener('click', async () => {
const code = document.querySelector('#code').textContent;
try {
await navigator.clipboard.writeText(code); // needs HTTPS and a user click
msg.textContent = 'Copied ✅';
} catch (err) {
msg.textContent = 'Copy blocked here – select and copy manually: ' + code;
}
});
document.querySelector('#share').addEventListener('click', async () => {
const data = { title: 'Kaju Katli – Sharma Sweets', text: 'Fresh kaju katli ₹900/kg 🍬', url: 'https://sharmasweets.in/menu#kaju' };
if (navigator.share) {
try { await navigator.share(data); msg.textContent = 'Shared!'; }
catch (err) { msg.textContent = 'Share cancelled'; }
} else {
msg.textContent = 'Web Share is not available on this device – use the WhatsApp link.';
}
});
const text = 'Namaste! I want to order 1 kg Kaju Katli for delivery tomorrow.';
document.querySelector('#wa').href = 'https://wa.me/919829012345?text=' + encodeURIComponent(text);
</script>URLs के अंदर spaces, &, ? और Hindi text encode करने होते हैं। encodeURIComponent “1 kg Kaju & Ghewar” को सुरक्षित string बनाता है – भूलने पर WhatsApp message पहले & पर टूट जाता है।
Geolocation: दुकान से दूरी
<button id="locate">How far am I from the shop?</button>
<p id="dist" role="status"></p>
<script>
const SHOP = { lat: 26.9196, lon: 75.8235 }; // Johari Bazaar, Jaipur
function km(a, b) { // Haversine formula
const R = 6371, rad = d => d * Math.PI / 180;
const dLat = rad(b.lat - a.lat), dLon = rad(b.lon - a.lon);
const h = Math.sin(dLat / 2) ** 2 + Math.cos(rad(a.lat)) * Math.cos(rad(b.lat)) * Math.sin(dLon / 2) ** 2;
return 2 * R * Math.asin(Math.sqrt(h));
}
document.querySelector('#locate').addEventListener('click', () => {
const out = document.querySelector('#dist');
if (!('geolocation' in navigator)) { out.textContent = 'Location not supported'; return; }
out.textContent = 'Asking for permission…';
navigator.geolocation.getCurrentPosition(
pos => {
const d = km(SHOP, { lat: pos.coords.latitude, lon: pos.coords.longitude });
out.textContent = `You are about ${d.toFixed(1)} km away. ${d <= 15 ? 'We deliver to you 🛵' : 'Courier for dry sweets 📦'}`;
},
err => { out.textContent = 'Location unavailable (' + err.message + '). Enter your PIN code instead.'; },
{ enableHighAccuracy: false, timeout: 8000, maximumAge: 600000 }
);
});
</script>| API | चाहिए | अच्छा तरीक़ा |
|---|---|---|
| Geolocation | HTTPS + अनुमति | Click के बाद ही पूछें, कारण बताएँ, PIN-code विकल्प दें |
| Clipboard write | HTTPS + user gesture | “Copied” पुष्टि दिखाएँ |
| Web Share | HTTPS, ज़्यादातर mobile | Feature-detect करके fallback link दें |
| Notifications | अनुमति | User के चुनने पर ही पूछें (जैसे “ghewar आए तो बताओ”) |
| Page Visibility | कुछ नहीं | document.hidden पर timers/polling रोकें |
🏅 आज के Job Skills
Practice tasks
🏋️ Practice – ख़ुद करें
Solution देखें
const cart = [{ name: 'Kaju Katli', qty: 2 }, { name: 'Ghewar', qty: 1 }, { name: 'Ladoo & Peda box', qty: 1 }];
const lines = ['Namaste Sharma Sweets 🙏', 'My order:', ...cart.map(i => `• ${i.name} × ${i.qty}`), 'Please confirm.'];
const url = 'https://wa.me/919829012345?text=' + encodeURIComponent(lines.join('\n'));
console.log(url);Quick quiz
🧠 Quick Quiz – ख़ुद को जाँचें
Interview corner
💼 Interview Corner
Infinite scroll कैसे लागू करेंगे?
पहला page render करें, list के बाद sentinel element रखकर उसे IntersectionObserver से observe करें (prefetch के लिए rootMargin)। Intersect होने पर अगला page fetch करें (duplicate requests से बचते हुए), items जोड़ें, और अंत में observe बंद करें। Back navigation के लिए URLs/state रखें और बहुत लंबी lists के लिए virtualisation सोचें।
Filters URL में क्यों रखें?
URLs share, bookmark हो सकते हैं, Back/Forward से चलते हैं, refresh में बचते हैं और SEO व analytics में मदद करते हैं। URLSearchParams + history.pushState/replaceState बिना reload UI state और URL को sync रखते हैं।
सारांश
- URL/URLSearchParams query strings सुरक्षित parse और build करते हैं।
- pushState + popstate बिना reload filters share-योग्य रखते हैं।
- Lazy load, infinite scroll और reveals के लिए IntersectionObserver।
- Clipboard, Share, WhatsApp links और geolocation – HTTPS, अनुमति और fallbacks के साथ।
Day 23: regular expressions – mobile numbers, PIN codes, GSTIN, e-mail और passwords सही से validate करें।
