এই পর্বে যা শিখবেন
- Secure context, transient user activation আর Permissions API — কোন API কখন, কেন অনুমতি চায়
- একটি নির্ভরযোগ্য “লিংক কপি করুন” বোতাম, আর অবস্থান থেকে নিকটতম পিকআপ পয়েন্ট খোঁজা (haversine সূত্রসহ)
- Notification বনাম push notification, Web Share, Vibration, Fullscreen, Screen Wake Lock আর Page Visibility
- IntersectionObserver, ResizeObserver আর MutationObserver — কখন চলে, কীভাবে কাজ করে, কোথায় লাগে
- Feature detection বনাম browser sniffing, progressive enhancement, Baseline আর fingerprinting-এর ঝুঁকি
১ · DOM-এর বাইরে — ব্রাউজার আসলে একটি পুরো প্ল্যাটফর্ম
পর্ব ৩৭ থেকে ৪০-এ আপনি DOM, event, form আর storage দেখেছেন — সবই পাতার ভেতরের জিনিস। কিন্তু আজকের একটি ই-কমার্স অ্যাপ আরও অনেক কিছু করে: “লিংক কপি করুন” চাপলে লিংক clipboard-এ যায়; “কাছের পিকআপ পয়েন্ট” চাপলে ফোনের অবস্থান থেকে দূরত্ব হিসাব হয়; অর্ডার রওনা হলে notification আসে; মোবাইলে “শেয়ার” চাপলে WhatsApp বা Messenger-এর তালিকা খোলে। এগুলোর কোনোটিই JavaScript ভাষার অংশ নয় — সবই ব্রাউজারের দেওয়া Web API.
সব API মুখস্থ করা সম্ভব নয়, দরকারও নেই। এই পর্বের লক্ষ্য একটি মানসিক কাঠামো: যেকোনো নতুন API হাতে পেলে চারটি প্রশ্ন — সব ব্রাউজারে আছে? https লাগে? ক্লিক লাগে? অনুমতি লাগে, আর না দিলে কী হবে?
📱 যন্ত্রের ক্ষমতা
Clipboard, Geolocation, Notification, Web Share, Vibration, Fullscreen, Screen Wake Lock — পাতাকে ফোন বা কম্পিউটারের কোনো সুবিধা ব্যবহার করতে দেয়। এগুলোর বেশিরভাগ অনুমতি বা ক্লিক চায়।
👀 দক্ষ পর্যবেক্ষণ
IntersectionObserver, ResizeObserver, MutationObserver, Page Visibility — ব্রাউজার নিজেই খবর দেয় কখন কিছু ঘটল। বারবার জিজ্ঞেস করার (polling) দরকার নেই। এগুলো সাধারণত কোনো অনুমতি চায় না।
ঢাকার একটি আবাসিক ভবনের কথা ভাবুন। লবি আর লিফট সবার জন্য — DOM-এর মতো। কিন্তু কারও ফ্ল্যাটে ঢুকতে তিনটি ধাপ। প্রথমে মূল গেট দিয়ে, দারোয়ানের খাতায় নাম লিখে আসতে হবে — পেছনের দেয়াল টপকে নয় (এটি secure context, https). তারপর আপনাকে নিজে এসে বেল বাজাতে হবে — কেউ দূর থেকে রিমোটে বেল বাজালে চলবে না (এটি user activation, একটি সত্যিকারের ক্লিক). শেষে ফ্ল্যাটের মালিক দরজা খুলবেন কি না, তা তাঁর ইচ্ছা (এটি permission). “না” বললে ভদ্র অতিথি বারবার বেল বাজান না — দারোয়ানের কাছে জিনিসটি রেখে যান। এটিই graceful fallback.
কেন এত সাবধানতা? একটি লিংকে ক্লিক করলেই অচেনা একজনের লেখা কোড আপনার যন্ত্রে চলতে শুরু করে — কোনো install নেই, কোনো যাচাই নেই। তাই ব্রাউজার প্রতিটি পাতাকে একটি sandbox-এ রাখে, আর ক্ষমতা দেয় ধাপে ধাপে, ব্যবহারকারীর হাতে নিয়ন্ত্রণ রেখে।
২ · তিনটি দরজা — secure context, user activation আর permission
প্রথম দরজা: secure context
যে পাতা https দিয়ে এসেছে, তাকে ব্রাউজার বলে secure context. যুক্তি সহজ: http পাতার কোড পথে যে কেউ বদলে দিতে পারে — একটি পাবলিক Wi-Fi-র রাউটারও। এমন পাতাকে অবস্থান দেওয়া মানে অচেনা মধ্যস্থতাকারীকে দেওয়া। তাই Geolocation, Clipboard-এর async API, Notification, Service Worker, Web Share, Screen Wake Lock — শুধু secure context-এ। Chrome ২০১৬ সালে (Chrome 50) http পাতা থেকে geolocation সরিয়ে দেয়।
যাচাই করতে window.isSecureContext দেখুন। ব্যতিক্রম: http://localhost আর http://127.0.0.1-কেও ব্রাউজার secure ধরে, যাতে ডেভেলপাররা certificate ছাড়াই কাজ করতে পারেন। কিন্তু একই অ্যাপ ফোনে http://192.168.0.5:5173 দিয়ে খুললে সেটি secure নয় — তখন clipboard বা geolocation হঠাৎ ব্যর্থ হয়, আর নতুনরা ভাবেন কোড ভাঙা। কারণটি কোড নয়, ঠিকানা।
দ্বিতীয় দরজা: user activation
কিছু কাজ শুধু তখনই করা যায়, যখন ব্যবহারকারী এইমাত্র পাতায় কিছু করেছেন — ক্লিক, ট্যাপ বা কী-প্রেস। একে বলে transient user activation (ক্ষণস্থায়ী সক্রিয়তা): ক্লিকের পরে অল্প কিছুক্ষণ (Chrome-এ প্রায় ৫ সেকেন্ড) পাতাটি “সক্রিয়” থাকে। Popup, fullscreen, অনেক ব্রাউজারে clipboard-এ লেখা, Web Share, vibration আর notification-এর অনুমতি চাওয়া — সবই এই নিয়মের অধীনে। নিয়মটি না থাকলে যেকোনো পাতা খোলামাত্র দশটি popup খুলত — ২০০০-এর দশকের ওয়েব ঠিক এমনই ছিল।
ব্যবহারিক ফল: এমন API click handler-এর ভেতর থেকে ডাকুন, আর তার আগে লম্বা await রাখবেন না। আগে কয়েক সেকেন্ডের একটি fetch থাকলে activation ফুরিয়ে যেতে পারে, আর navigator.share ব্যর্থ হবে NotAllowedError দিয়ে। ডেটা আগেই তৈরি রাখুন।
তৃতীয় দরজা: permission, আর Permissions API
সবচেয়ে সংবেদনশীল API — অবস্থান, notification, ক্যামেরা, মাইক্রোফোন, clipboard থেকে পড়া — ব্যবহারকারীর স্পষ্ট অনুমতি চায়। প্রথমবার ডাকলে ব্রাউজার নিজে একটি permission prompt দেখায়: “abcltech.com আপনার অবস্থান জানতে চায় — Allow / Block”. এই বাক্সের লেখা বা নকশা আপনি বদলাতে পারবেন না — ইচ্ছাকৃতভাবেই। ব্যবহারকারী Block করলে সিদ্ধান্তটি সাধারণত মনে রাখা হয়; ব্যবহারকারী নিজে সেটিংসে না বদলালে আপনার কোড আর prompt দেখাতে পারবে না।
তাই prompt দেখানোর আগেই জানা দরকার অবস্থা কী। navigator.permissions.query() তিনটি অবস্থার একটি জানায়: "granted" (আগেই অনুমতি আছে), "denied" (ব্লক করা), "prompt" (এখনো জিজ্ঞেস করা হয়নি — ডাকলে prompt আসবে)।
async function locationButtonLabel() {
if (!navigator.permissions) return "কাছের পিকআপ খুঁজুন"; // পুরোনো ব্রাউজার
const status = await navigator.permissions.query({ name: "geolocation" });
status.onchange = () => updateButton(status.state); // সেটিংসে বদলালেও জানব
if (status.state === "granted") return "📍 কাছের পিকআপ (অবস্থান চালু)";
if (status.state === "denied") return "এলাকা বেছে নিন"; // আর জিজ্ঞেস নয়
return "📍 অবস্থান দিয়ে কাছের পিকআপ খুঁজুন"; // "prompt"
}
// প্রত্যাশিত: প্রথমবার "prompt" → তৃতীয় লেখা; Allow করার পরে "granted"লক্ষ করুন, query() নিজে কখনো prompt দেখায় না — শুধু অবস্থা পড়ে। সব নাম সব ব্রাউজারে চলে না (যেমন "clipboard-write" শুধু Chromium-ভিত্তিক ব্রাউজারে বোঝে, অন্যত্র TypeError), তাই query()-কে try/catch-এ রাখুন। আর "denied" হলে বোতামটি লুকাবেন না — বিকল্প পথ দেখান: “এলাকা বেছে নিন”.
এবার পুরো প্রবাহটি একসাথে দেখুন — একটি “কাছের পিকআপ পয়েন্ট খুঁজুন” বোতাম চাপা থেকে ফল পর্যন্ত:
- API আছে?
'geolocation' in navigatorfeature detect - https?
isSecureContextsecure context - ক্লিক
onclickuser activation - Prompt
Allow / Blockব্রাউজারের বাক্স - সিদ্ধান্ত
granted / deniedsuccess বা error - Fallback
এলাকা বাছুনসম্মানজনক বিকল্প
if (!('geolocation' in navigator)) {
showAreaPicker(); // সরাসরি বিকল্পে
return;
}
isSecureContext // https://shop.example.com → true // http://localhost → true // http://192.168.0.5 → false
findBtn.addEventListener('click', () => {
navigator.geolocation.getCurrentPosition(ok, fail);
}); // পাতা খোলামাত্র নয়!
┌───────────────────────────────────────┐ shop.example.com আপনার অবস্থান জানতে চায় [ Block ] [ Allow ] └───────────────────────────────────────┘
ok(pos) → pos.coords.latitude, longitude, accuracy fail(err) → err.code === 1 (PERMISSION_DENIED) 2 = POSITION_UNAVAILABLE, 3 = TIMEOUT
fail = (err) => {
showAreaPicker(); // মিরপুর, ধানমন্ডি, উত্তরা…
note('অবস্থান ছাড়াও এলাকা বেছে খুঁজতে পারেন');
};
- API আছে কি না দেখুন।
'geolocation' in navigator— ব্রাউজারের নাম দেখে নয়, ক্ষমতা দেখে সিদ্ধান্ত। না থাকলে বোতামটিকে সরাসরি “এলাকা বেছে নিন”-এ পাঠান। এটি progressive enhancement: মূল কাজ (পিকআপ খোঁজা) সবার জন্য চলে, অবস্থান শুধু বাড়তি সুবিধা। - Secure context. http পাতায় শক্তিশালী API হয় একেবারে অনুপস্থিত, নয়তো সাথে সাথে ব্যর্থ হয়। প্রোডাকশন সবসময় https-এ; ফোনে পরীক্ষা করতে LAN ঠিকানা ব্যবহার করলে মনে রাখুন সেটি secure নয়।
- ব্যবহারকারীর কাজের উত্তরে চান। বোতামের লেখা নিজেই ব্যাখ্যা: “অবস্থান দিয়ে কাছের পিকআপ খুঁজুন”. ব্যবহারকারী জেনে চাপছেন, তাই prompt এলে তিনি অবাক হন না — আর হ্যাঁ বলার সম্ভাবনা অনেক বেশি। পাতা খোলামাত্র prompt দেখানো সবচেয়ে বড় ভুল।
- ব্রাউজারের prompt. নিয়ন্ত্রণ এখন ব্যবহারকারীর হাতে। ব্যবহারকারী বাক্সটি উপেক্ষা করলে callback অনেকক্ষণ না-ও আসতে পারে — তাই
timeoutদিন, আর “খোঁজা হচ্ছে…” দেখান। - সিদ্ধান্ত। Allow হলে success callback-এ অবস্থান; Block হলে error callback-এ
code 1. GPS সংকেত না পেলেcode 2, সময় পেরোলেcode 3— এগুলোও স্বাভাবিক ঘটনা। - Fallback — সম্মানজনক বিকল্প। “না” বললে শাস্তি নয়: পাতা আটকে রাখা বা বারবার জিজ্ঞেস করা ভুল। এলাকার তালিকা দেখান, কাজ চালিয়ে যান।
প্রসঙ্গে চান — যে মুহূর্তে ব্যবহারকারী সেই সুবিধাটি চাইছেন। আগে বোঝান — নিজের পাতায় এক লাইনে বলুন কেন দরকার (“কাছের পিকআপ দেখাতে অবস্থান লাগবে; আমরা তা সংরক্ষণ করি না”), তারপর ব্রাউজারের prompt. কম চান — শুধু যা দরকার; একসাথে অবস্থান, notification আর ক্যামেরা নয়।
৩ · Clipboard API — একটি নির্ভরযোগ্য “লিংক কপি করুন” বোতাম
পণ্যের পাতায় “লিংক কপি করুন” বোতাম খুব সাধারণ। আধুনিক Clipboard API দুটি সহজ method দেয়: navigator.clipboard.writeText(text) আর navigator.clipboard.readText(). দুটোই async — একটি Promise ফেরত দেয়, কারণ ব্রাউজারকে অনুমতি যাচাই করতে হতে পারে।
লেখা আর পড়ার নিরাপত্তা-নিয়ম আলাদা, আর পার্থক্যটি যুক্তিসংগত। লেখা তুলনামূলক নিরীহ — বড়জোর ব্যবহারকারীর আগের কপি করা জিনিস মুছে যায়; তাই সাধারণত একটি ক্লিকই যথেষ্ট (Chrome-এ পাতা focus-এ থাকলেই চলে, Firefox আর Safari-তে user activation লাগে)। পড়া বিপজ্জনক — clipboard-এ হয়তো এইমাত্র কপি করা পাসওয়ার্ড, OTP বা bKash নম্বর আছে। তাই readText()-এ Chrome অনুমতি চায়, আর Safari ও Firefox একটি ছোট “Paste” বোতাম দেখায়, যা ব্যবহারকারীকে নিজে চাপতে হয়।
copyBtn.addEventListener("click", async () => {
const url = location.href;
try {
if (!navigator.clipboard?.writeText) throw new Error("no-clipboard-api");
await navigator.clipboard.writeText(url); // ক্লিকের ভেতরেই, আগে কোনো লম্বা await নেই
showToast("✅ লিংক কপি হয়েছে");
} catch (err) {
// http পাতা, পুরোনো ব্রাউজার, iframe-এর নিষেধ বা ব্যবহারকারীর নিষেধ — সব এখানে
linkInput.value = url;
linkInput.hidden = false;
linkInput.select(); // অন্তত সিলেক্ট করে দিই
showToast("কপি করা গেল না — লিংকটি সিলেক্ট করা আছে, Ctrl+C চাপুন");
}
});
// প্রত্যাশিত: https-এ "✅ লিংক কপি হয়েছে"; ব্যর্থ হলে সিলেক্ট-করা লিংক আর নির্দেশনাতিনটি জিনিস খেয়াল করুন। এক, API না থাকাও একই catch-এ যায় — সব ব্যর্থতার পথ একটিই। দুই, ব্যর্থ হলেও ব্যবহারকারী খালি হাতে ফেরেন না: লিংক সিলেক্ট করা থাকে। তিন, কপি একটি অদৃশ্য কাজ, তাই সফলতার বার্তা জরুরি। স্ক্রিন রিডার ব্যবহারকারীর জন্য toast-টি aria-live="polite" অংশে রাখুন (accessibility).
পুরোনো পথ: document.execCommand('copy')
Clipboard API আসার আগে (Chrome-এ ২০১৮ সালে) উপায় ছিল একটি লুকানো <textarea>-এ লেখা সিলেক্ট করে document.execCommand('copy'). এটি synchronous, ফলাফল শুধু true/false, আর MDN একে deprecated বলে। কিছু লাইব্রেরি এটিকে fallback হিসেবে রাখে, কিন্তু নতুন কোডের প্রধান পথ নয়। এই কোর্সের কোড-বক্সের “কপি” বোতামও আগে navigator.clipboard চেষ্টা করে, না পেলে তবেই execCommand.
গোপনীয়তা: ব্যবহারকারীর অজান্তে clipboard পড়বেন না, আর কপি করা লেখার সাথে লুকিয়ে ট্র্যাকিং লিংক জুড়ে দেবেন না। ২০২০ সালে iOS 14 যখন clipboard পড়লে নোটিশ দেখাতে শুরু করে, তখন অনেক জনপ্রিয় অ্যাপ ধরা পড়ে যায়, যারা নীরবে clipboard পড়ছিল।
৪ · Geolocation — অবস্থান থেকে নিকটতম পিকআপ পয়েন্ট
Geolocation API যন্ত্রের অবস্থান দেয় — অক্ষাংশ (latitude), দ্রাঘিমাংশ (longitude) আর নির্ভুলতা (accuracy, মিটারে)। অবস্থান কোথা থেকে আসছে, তা ব্রাউজার আপনাকে বলে না: ফোনে GPS হতে পারে (কয়েক মিটার নির্ভুল), ল্যাপটপে আশেপাশের Wi-Fi নেটওয়ার্ক বা IP ঠিকানা থেকে অনুমান (কয়েকশো মিটার থেকে কয়েক কিলোমিটার ভুল)। তাই accuracy সবসময় দেখুন।
| Method | কী করে | কখন |
|---|---|---|
getCurrentPosition(ok, fail, opts) | একবার অবস্থান দেয় | নিকটতম দোকান, ঠিকানা পূরণ |
watchPosition(ok, fail, opts) | অবস্থান বদলালেই বারবার দেয়; একটি id ফেরত দেয় | ডেলিভারি রাইডারের লাইভ ম্যাপ, হাঁটার দিকনির্দেশ |
clearWatch(id) | নজরদারি বন্ধ | কাজ শেষ হলে — নইলে ব্যাটারি খায় |
তিনটি option: enableHighAccuracy: true (GPS চালু করে — বেশি নির্ভুল, কিন্তু ধীর আর ব্যাটারি বেশি খায়), timeout (কত ms-এর মধ্যে উত্তর না এলে code 3), maximumAge (কত পুরোনো cache-করা অবস্থা চলবে)। আর error-এর তিনটি code: 1 PERMISSION_DENIED, 2 POSITION_UNAVAILABLE (যন্ত্র অবস্থান বের করতে পারেনি — ঘরের ভেতরে, বা লোকেশন সার্ভিস বন্ধ), 3 TIMEOUT.
দূরত্ব হিসাব — haversine সূত্র
অবস্থান পেলে পরের কাজ গণিত: চারটি পিকআপ পয়েন্টের কোনটি সবচেয়ে কাছে? অক্ষাংশ-দ্রাঘিমাংশ সমতলের x-y নয়, গোলকের ওপরের বিন্দু — তাই সাধারণ পিথাগোরাস কাজ করে না (দ্রাঘিমাংশের এক ডিগ্রি বিষুবরেখায় প্রায় ১১১ km, কিন্তু মেরুর কাছে প্রায় শূন্য)। গোলকের ওপর দুটি বিন্দুর সবচেয়ে ছোট দূরত্ব (great-circle distance) বের করার প্রচলিত উপায় haversine সূত্র। পৃথিবী নিখুঁত গোলক নয়, তাই সামান্য (প্রায় ০.৫%-এর মধ্যে) ভুল থাকে — দোকান খোঁজার জন্য যথেষ্ট। আর এটি সোজা দূরত্ব, রাস্তার নয়।
নিচের কোড এই পর্বের মূল শিক্ষা একসাথে দেখায়: feature detection, অনুমতি-নির্ভর API চেষ্টা, ব্যর্থ হলে বিকল্প, আর বিশুদ্ধ গণিত। কোডটি চলে একটি sandbox-করা iframe-এ, যার অনুমতি দেওয়ার ক্ষমতা নেই — তাই এখানে clipboard আর geolocation ইচ্ছাকৃতভাবে ব্যর্থ হবে, আর আপনি দেখবেন কোড সেটি কীভাবে সামলায়। স্থানাঙ্কগুলো আনুমানিক।
const rad = (deg) => (deg * Math.PI) / 180;
function km(a, b) { // haversine: গোলকের ওপর সোজা দূরত্ব
const h = Math.sin(rad(b.lat - a.lat) / 2) ** 2 +
Math.cos(rad(a.lat)) * Math.cos(rad(b.lat)) * Math.sin(rad(b.lon - a.lon) / 2) ** 2;
return 2 * 6371 * Math.asin(Math.sqrt(h)); // 6371 km = পৃথিবীর গড় ব্যাসার্ধ
}
const POINTS = [{ name: 'ধানমন্ডি ২৭', lat: 23.7561, lon: 90.3742 }, { name: 'আগারগাঁও', lat: 23.7783, lon: 90.3797 },
{ name: 'উত্তরা ৭', lat: 23.8719, lon: 90.3984 }, { name: 'মতিঝিল', lat: 23.7330, lon: 90.4172 }];
const nearest = (me) => POINTS.map((p) => ({ name: p.name, km: +km(me, p).toFixed(1) })).toSorted((x, y) => x.km - y.km);
const getPosition = () => new Promise((ok, fail) =>
navigator.geolocation.getCurrentPosition(ok, fail, { timeout: 5000 }));
(async () => {
console.log('secure context:', isSecureContext, '| geolocation আছে:', 'geolocation' in navigator,
'| Notification:', window.Notification?.permission ?? 'নেই');
try { await navigator.clipboard.writeText('https://shop.example.com/p/42'); console.log('লিংক কপি হয়েছে'); }
catch (e) { console.log('কপি হলো না:', e.name, '→ লিংকটি সিলেক্ট করে দেখান'); }
let me = { lat: 23.8069, lon: 90.3687 }; // বিকল্প: তালিকা থেকে বাছা "মিরপুর ১০"
try { const { coords } = await getPosition(); me = { lat: coords.latitude, lon: coords.longitude }; }
catch (e) { console.log('অবস্থান মিলল না: code', e.code ?? 'API নেই', '→ হাতে বাছা এলাকা ব্যবহার'); }
console.log(nearest(me).slice(0, 2));
})();Edge/Chrome-এ আউটপুট চার লাইন। প্রথম লাইন secure context: true | geolocation আছে: true | Notification: denied — API গুলো আছে, পাতাও secure, তবু sandbox-এর ভেতরে notification-এর অবস্থা সরাসরি denied. দ্বিতীয় লাইন কপি হলো না: NotAllowedError → … — sandbox-করা iframe-এর clipboard-এ লেখার অধিকার নেই, তাই promise reject হলো; কোড ভেঙে পড়েনি, বিকল্প বার্তা দিয়েছে। তৃতীয় লাইন অবস্থান মিলল না: code 1 → … — Edge-এ বার্তাটি “Geolocation has been disabled in this document by permissions policy.”, অর্থাৎ Permissions Policy এই iframe-কে অবস্থান চাওয়ার অধিকারই দেয়নি, তাই কোনো prompt-ও আসেনি। শেষে মিরপুর ১০ থেকে [ { name: 'আগারগাঁও', km: 3.4 }, { name: 'ধানমন্ডি ২৭', km: 5.7 } ]. একটি সাধারণ https পাতায় একই কোড চালালে ক্লিক থেকে ডাকা writeText সফল হতো, আর getCurrentPosition ব্যবহারকারীকে prompt দেখাত — Allow করলে আসল অবস্থান থেকে দূরত্ব আসত। অন্য ব্রাউজারে error-এর বার্তা আলাদা হতে পারে, কিন্তু নাম আর code একই।
অবস্থান খুব ব্যক্তিগত তথ্য। কয়েক দিনের অবস্থান থেকেই বোঝা যায় একজন কোথায় থাকেন, কাজ করেন, কোন হাসপাতালে যান। তাই দরকারের মুহূর্তে একবার নিন, সার্ভারে পাঠাতে হলে কম নির্ভুলতায় পাঠান, আর হিসাবটি সম্ভব হলে ব্রাউজারেই করুন — উপরের কোডের মতো।
৫ · Notification, Web Share আর যন্ত্রের অন্যান্য ক্ষমতা
Notification বনাম push notification
Notifications API অপারেটিং সিস্টেমের notification দেখায় — ফোনের ওপরের বার বা Windows-এর কোণের বাক্স। দুই ধাপ: অনুমতি চাওয়া (Notification.requestPermission(), ফল "granted", "denied" বা "default"), তারপর new Notification(title, { body, icon }). মনে রাখবেন, এখানে অবস্থার নাম "default" — Permissions API-র "prompt"-এর সমান।
notifyBtn.addEventListener("click", async () => { // ক্লিকের উত্তরে চাওয়া
if (!("Notification" in window)) return showInPageBanner();
const result = await Notification.requestPermission(); // "granted" | "denied" | "default"
if (result !== "granted") return showInPageBanner(); // পাতার ভেতরেই জানাব
new Notification("অর্ডার #4521 রওনা হয়েছে", { body: "রাইডার ৩০ মিনিটে পৌঁছাবেন", tag: "order-4521" });
});
// প্রত্যাশিত: Allow করলে সিস্টেম notification; Block করলে পাতার ভেতরের ব্যানারবড় সীমাবদ্ধতা: new Notification() কাজ করে শুধু পাতা খোলা থাকলে। ট্যাব বন্ধ থাকলে জানাতে লাগে push notification — সার্ভার push service-এর মাধ্যমে বার্তা পাঠায়, আর ব্রাউজার একটি service worker-কে জাগায়, যে registration.showNotification() ডাকে। Android-এর Chrome-এ new Notification() সরাসরি TypeError দেয় — সেখানে এটিই একমাত্র পথ। বিস্তারিত পর্ব ৪৪-এ। tag দিলে একই অর্ডারের নতুন notification পুরোনোটিকে বদলে দেয়।
Web Share — মোবাইলের নিজস্ব শেয়ার তালিকা
মোবাইলে Web Share API অপারেটিং সিস্টেমের শেয়ার শিট খোলে — ফোনে থাকা WhatsApp, Messenger, Gmail, যেকোনো অ্যাপ; প্রতিটির জন্য আলাদা বোতাম বা তৃতীয় পক্ষের script লাগে না। ডেস্কটপে সব ব্রাউজারে নেই — তাই feature detection, আর না থাকলে “লিংক কপি” বোতাম।
const data = { title: "জামদানি শাড়ি — ৳৮,৫০০", text: "এটা দেখো!", url: productUrl };
shareBtn.addEventListener("click", async () => {
if (!navigator.canShare?.(data)) return copyLink(productUrl); // ডেস্কটপ: কপি-বোতামে ফিরি
try { await navigator.share(data); } // ক্লিকের ভেতরে, আগে await নেই
catch (err) { if (err.name !== "AbortError") copyLink(productUrl); } // বাতিল করা ভুল নয়
});
// প্রত্যাশিত: মোবাইলে শেয়ার শিট খোলে; ব্যবহারকারী বন্ধ করলে AbortError — চুপচাপ উপেক্ষাব্যবহারকারী শেয়ার শিট খুলে মত বদলে বন্ধ করলে promise reject হয় AbortError দিয়ে — পর্ব ৩৬-এর মতোই, এটি ভুল নয়, তাই কোনো error বার্তা দেখানো উচিত নয়। canShare(data) আগে জানিয়ে দেয় এই ডেটা (যেমন ফাইলসহ) শেয়ার করা যাবে কি না।
আরও চারটি ছোট কিন্তু কাজের API
| API | এক লাইনে | বাস্তব ব্যবহার | খেয়াল রাখুন |
|---|---|---|---|
Vibration — navigator.vibrate(200) | ফোন কাঁপায় (ms-এ, বা [200, 100, 200] ছন্দ) | পেমেন্ট সফল হলে ছোট একটি কাঁপুনি | iPhone-এর Safari-তে নেই; ক্লিক লাগে; শুধু ইঙ্গিত, একমাত্র সংকেত নয় |
Fullscreen — el.requestFullscreen() | একটি element পুরো পর্দায় | পণ্যের ছবি, ভিডিও, ড্যাশবোর্ড | ক্লিক লাগে; Esc দিয়ে সবসময় বেরোনো যায়; iPhone-এ সাধারণ element-এ সীমিত |
Screen Wake Lock — navigator.wakeLock.request("screen") | পর্দা নিজে থেকে নিভতে দেয় না | রান্নার রেসিপি, টিকিটের QR কোড দেখানো | https লাগে; ট্যাব লুকালে lock নিজে ছেড়ে যায় — ফিরলে আবার চাইতে হয় |
Page Visibility — document.hidden | ট্যাব দেখা যাচ্ছে কি না | পেছনের ট্যাবে polling বা ভিডিও থামানো | কোনো অনুমতি লাগে না — পরের অংশে বিস্তারিত |
৬ · Page Visibility — কেউ না দেখলে কাজ থামান
একটি শেয়ারবাজারের দামের পাতা ভাবুন, যা প্রতি ৫ সেকেন্ডে দাম আনে। ব্যবহারকারী অন্য ট্যাবে গিয়ে ভুলে গেলেন — পাতাটি সারা রাত request পাঠাচ্ছে, কেউ দেখছে না: মোবাইল ডেটা, ব্যাটারি আর সার্ভারের অপচয়। ব্রাউজার নিজেও পেছনের ট্যাবের timer ধীর করে (Chrome-এ সাধারণত সেকেন্ডে বড়জোর একবার), কিন্তু সেটির ওপর ভরসা না করে নিজেই থামানো ভালো।
document.visibilityState হয় "visible", নয়তো "hidden"; বদলালে visibilitychange event আসে। ট্যাব বদল, ব্রাউজার minimize, ফোনের পর্দা লক — সবই "hidden".
let timer = null;
function startPolling() {
refreshPrice(); // ফিরে এসেই তাজা দাম
timer = setInterval(refreshPrice, 5000);
}
function stopPolling() { clearInterval(timer); timer = null; }
document.addEventListener("visibilitychange", () => {
if (document.hidden) stopPolling(); // কেউ দেখছে না — থামো
else startPolling(); // আবার দেখা যাচ্ছে — চালু
});
if (!document.hidden) startPolling();
// প্রত্যাশিত: অন্য ট্যাবে গেলে request বন্ধ; ফিরলে সাথে সাথে একবার, তারপর প্রতি ৫ সেকেন্ডেএকই ধারণা ভিডিও বা ক্যারোসেল থামাতে, আর analytics পাঠাতে খাটে। মোবাইলে ব্যবহারকারী প্রায়ই ট্যাব বন্ধ না করে অ্যাপ বদলান, পরে সিস্টেম পাতাটি মেরে ফেলে — তখন unload event আসেই না। তাই শেষ অবস্থা পাঠানো বা অসমাপ্ত ফর্ম সেভের নির্ভরযোগ্য মুহূর্ত হলো hidden হওয়া, সাথে navigator.sendBeacon().
৭ · IntersectionObserver — কোনো element কখন দেখা দিল
একটি পণ্যের তালিকায় ২০০টি ছবি। সব ছবি একসাথে নামালে প্রথম পর্দা দেখাতেই অনেক দেরি, আর ব্যবহারকারী যেগুলো পর্যন্ত কখনো স্ক্রল করবেন না, সেগুলোর জন্যও ডেটা খরচ। সমাধান lazy loading: ছবি পর্দার কাছে এলে তবেই নামানো। প্রশ্ন হলো, “পর্দার কাছে এলো” কীভাবে জানবেন?
পুরোনো উপায় ছিল scroll event-এ প্রতিটি ছবির getBoundingClientRect() দেখা। scroll event সেকেন্ডে কয়েক ডজন বার আসে, প্রতিবার অবস্থান মাপতে ব্রাউজারকে layout হিসাব করতে হয়, আর সব চলে main thread-এ — ঠিক যখন ব্যবহারকারী মসৃণ স্ক্রল চাইছেন। পর্ব ৩৬-এর throttle কিছুটা সাহায্য করে, কিন্তু আপনি তবু বারবার জিজ্ঞেস করছেন, “এখন দেখা যাচ্ছে? এখন?”
IntersectionObserver প্রশ্নটি উল্টে দেয়: আপনি বলেন “এই element দেখা দিলে জানিও”, আর ব্রাউজার — যে প্রতিটি frame আঁকার সময় এমনিতেই সব element-এর অবস্থান জানে — শুধু সীমা পার হওয়ার মুহূর্তে callback ডাকে। Scroll চলাকালীন আপনার কোড একবারও চলে না।
const io = new IntersectionObserver((entries, observer) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
const img = entry.target;
img.src = img.dataset.src; // এবার আসল ছবি নামাও
observer.unobserve(img); // কাজ শেষ — আর নজর নয়
}
}, { rootMargin: "200px 0px", threshold: 0 }); // পর্দার ২০০px আগেই শুরু
document.querySelectorAll("img[data-src]").forEach((img) => io.observe(img));তিনটি option: root — কোন এলাকার সাপেক্ষে দেখা (ডিফল্ট null মানে browser viewport; কোনো scroll হওয়া div-ও দেওয়া যায়); rootMargin — সেই এলাকাকে বড় বা ছোট করা, CSS margin-এর মতো লিখে ("200px 0px" মানে ওপরে-নিচে ২০০px আগেই “দেখা গেছে” ধরা, যাতে ব্যবহারকারী পৌঁছানোর আগেই ছবি নেমে যায়); threshold — element-এর কত অংশ দেখা গেলে callback (০ মানে এক pixel, ১ মানে পুরোটা, [0, 0.5, 1] দিলে তিনবার)। একটি observer দিয়েই হাজার element দেখা যায়।
একটি ছবি নিচ থেকে পর্দার দিকে উঠে আসার সময় ভেতরে কী ঘটে, ধাপে ধাপে দেখুন (viewport-এর উচ্চতা ৮০০px, rootMargin: "200px", তাই আসল সীমা ১০০০px):
1io.observe(img);2// … ব্যবহারকারী স্ক্রল করছেন …3(entries) => {4 if (!entry.isIntersecting) continue;5 img.src = img.dataset.src;6 observer.unobserve(img);7}
| ছবির ওপরের প্রান্ত (viewport থেকে) | |
| isIntersecting | |
| callback কতবার | |
| img.src | |
| scroll-এ আপনার কোড চলল |
- observe — আর একটি প্রাথমিক callback.
observe()-এর পরে ব্রাউজার প্রথম সুযোগেই একবার callback ডাকে বর্তমান অবস্থা জানাতে — এখানেisIntersecting: false, কারণ ছবি অনেক নিচে। তাই callback-এ সবসময়isIntersectingযাচাই করুন। - স্ক্রল চলছে — কিছুই ঘটছে না। ছবি ১৫০০px-এ উঠে এলো, কয়েকশো scroll event গেল, কিন্তু আপনার কোড একবারও চলেনি — ব্রাউজার frame আঁকার সময় নিজেই সীমা দেখছে। scroll listener হলে এখানে শত শত বার function চলত।
- সীমা পার — callback.
rootMargin-এর কারণে আসল সীমা viewport-এর ২০০px নিচে, অর্থাৎ ১০০০px-এ। ছবির ওপরের প্রান্ত ৯৯০px-এ আসতেইisIntersecting: trueহলো, আর ব্রাউজার একটি task হিসেবে callback পাঠাল। - src বসানো, তারপর unobserve.
img.srcবসাতেই নেটওয়ার্ক request শুরু। তারপরunobserve(img)— নইলে প্রতিবার ঢোকা-বেরোনোয় অকারণে callback আসত। - ব্যবহারকারী পৌঁছালেন — ছবি তৈরি। পুরো যাত্রায় callback মাত্র দুবার, scroll-এ আপনার কোড শূন্যবার। Infinite scroll-এ একই কৌশল: তালিকার শেষে একটি অদৃশ্য “sentinel” div রাখুন; সেটি দেখা দিলে পরের পাতা আনুন।
loading="lazy" — অনেক সময় JavaScript-ই লাগে না
শুধু ছবি বা iframe দেরিতে নামাতে এখন HTML-ই যথেষ্ট: <img src="saree.webp" loading="lazy" width="300" height="400" alt="…">. সব আধুনিক ব্রাউজার এটি বোঝে, আর দূরত্বের সীমা নিজেই ঠিক করে। দুটি সতর্কতা: প্রথম পর্দার ছবিতে loading="lazy" দেবেন না — Core Web Vitals-এর LCP খারাপ হয়; আর width/height দিন, নইলে লেখা লাফিয়ে সরে যায়। IntersectionObserver লাগে যখন “দেখা দিল” মুহূর্তে আরও কিছু করতে হয়: infinite scroll, অ্যানিমেশন, ভিডিও চালানো-থামানো, বা “seen” analytics — একটি পণ্যের কার্ড সত্যিই চোখে পড়েছে কি না।
৮ · ResizeObserver আর MutationObserver — আকার আর DOM-এর পরিবর্তন
ResizeObserver — element-এর নিজের আকার
window-এর resize event শুধু পুরো জানালার কথা জানায়। কিন্তু একটি পণ্যের কার্ডের আকার জানালা না বদলেও বদলাতে পারে: ফিল্টার-প্যানেল খুললে, বা একই কার্ড সরু sidebar-এ বসালে। ResizeObserver প্রতিটি element-এর নিজের আকার দেখে, আর বদলালে callback-এ contentRect দেয় — কার্ডটি নিজের জায়গা বুঝে layout বদলাতে পারে।
শুধু style বদলানোর কাজ হলে আজ CSS container queries (@container (width < 400px) { … }) আরও ভালো, যা ২০২৩ সাল থেকে সব প্রধান ব্রাউজারে চলে। ResizeObserver লাগে যখন আকার অনুযায়ী JavaScript-এর কিছু করতে হয় — chart নতুন করে আঁকা, canvas-এর resolution ঠিক করা, বা কতগুলো ট্যাব “আরও” মেনুতে যাবে তা হিসাব।
MutationObserver — DOM কখন বদলাল
কখনো এমন DOM-এ নজর রাখতে হয়, যা আপনার কোড বদলায় না: একটি তৃতীয় পক্ষের chat widget বা পেমেন্ট গেটওয়ের embed নিজের মতো element যোগ করে। MutationObserver child যোগ-বিয়োগ (childList), attribute বদল (attributes) বা লেখা বদল (characterData) হলে জানায় — subtree: true দিলে পুরো উপ-গাছে। আগের প্রজন্ম “Mutation Events” (DOMNodeInserted ইত্যাদি) প্রতিটি পরিবর্তনে synchronous-ভাবে চলত আর DOM-কে ভীষণ ধীর করত; Chrome ২০২৪ সালে সেগুলো সরিয়ে দেয়। MutationObserver-এর মূল কৌশল batching: পরিবর্তন জমিয়ে একটি microtask-এ একবারে জানানো।
1mo.observe(cart, { childList: true, attributes: true, subtree: true });2cart.append(li1); // বদল ১3cart.append(li2); // বদল ২4li1.dataset.qty = "2"; // বদল ৩5console.log("তিনটি বদল শেষ");6// → mo-এর callback(records)
| জমা record | |
| microtask queue | |
| callback কতবার |
- নজর শুরু।
observe()জানায় কী কী দেখতে হবে। IntersectionObserver-এর মতো এখানে কোনো প্রাথমিক callback নেই — কিছু না বদলালে কিছুই আসে না। - বদল ১ — record জমা, একটি microtask নির্ধারিত।
append-এর সাথে সাথে callback চলে না। ব্রাউজার একটিMutationRecord(type: "childList") খাতায় জমা রাখে, আর microtask queue-তে একটি কাজ রাখে: “পরে observer-দের জানাও”. - বদল ২ — শুধু record, নতুন microtask নয়। খাতায় দ্বিতীয় record জমল। কাজটি আগেই queue-তে আছে, তাই নতুন করে রাখা হলো না।
- বদল ৩ — attribute.
dataset.qtyবসানো মানেdata-qtyattribute বদল — তৃতীয় record,type: "attributes",attributeName: "data-qty".subtree: trueদেওয়া আছে বলেইli-এর পরিবর্তনও ধরা পড়ল। - Synchronous কোড শেষ হয় আগে।
console.logচলল — callback তখনো চলেনি। পর্ব ৩১-এর নিয়ম: microtask চলে শুধু তখন, যখন বর্তমান কোড শেষ হয়ে call stack খালি হয়। - Microtask — একটি callback, তিনটি record. Stack খালি হতেই microtask চলল: observer-এর খাতা খালি করে তিনটি record একটি array-তে callback-কে দেওয়া হলো। ১০০০টি
liযোগ করলেও callback একবারই। সতর্কতা: callback-এর ভেতরে দেখা element-টিই বদলালে নতুন record তৈরি হয় — অসাবধান হলে অসীম চক্র।
এবার তিনটি observer নিজে চালিয়ে দেখুন। ওপরে একটি ছোট পাতা দেখা যাবে: খালি কার্ট-তালিকা, একটি পণ্যের কার্ড আর একটি scroll-বাক্স, যার নিচে একটি “ছবি” লুকানো। কোড নিজেই সব বদল করে, যাতে প্রতিবার একই আউটপুট আসে।
const $ = (sel) => document.querySelector(sel);
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
const mo = new MutationObserver((recs) => // ১) MutationObserver
console.log(`Mutation: ${recs.length}টি record —`, recs.map((r) => r.type).join(', ')));
mo.observe($('#cart'), { childList: true, attributes: true, subtree: true });
$('#cart').insertAdjacentHTML('beforeend', '<li>চাল</li>'); // বদল ১
$('#cart').insertAdjacentHTML('beforeend', '<li>ডাল</li>'); // বদল ২
$('#cart li').dataset.qty = '2'; // বদল ৩
console.log('তিনটি বদল শেষ — callback এখনো চলেনি');
new ResizeObserver(([entry]) => { // ২) ResizeObserver
const w = Math.round(entry.contentRect.width);
entry.target.className = w < 200 ? 'narrow' : 'wide';
console.log(`Resize: ${w}px → ${entry.target.className}`);
}).observe($('#card'));
const io = new IntersectionObserver(([entry]) => { // ৩) IntersectionObserver
console.log('Intersect:', entry.isIntersecting ? 'দেখা যাচ্ছে — এবার লোড' : 'এখনো দেখা যায় না');
if (entry.isIntersecting) { entry.target.textContent = 'ছবি লোড হলো'; io.unobserve(entry.target); }
}, { root: $('#feed') });
(async () => {
await wait(150); $('#card').style.width = '150px'; // কার্ড সরু হলো
await wait(150); io.observe($('#img'));
await wait(150); $('#feed').scrollTop = 999; // ব্যবহারকারী নিচে স্ক্রল করলেন
})();আউটপুট ছয় লাইন, এই ক্রমে। তিনটি বদল শেষ — callback এখনো চলেনি আগে আসে, তারপর Mutation: 3টি record — childList, childList, attributes — ঠিক stepper-এর মতো: তিনটি বদল, একটি microtask, একটি callback. তারপর Resize: 320px → wide — ResizeObserver-ও observe()-এর পরে প্রথম frame-এ একবার বর্তমান আকার জানায়; ১৫০ ms পরে কার্ড সরু হলে Resize: 150px → narrow (রং বদলে যায়)। শেষে Intersect: এখনো দেখা যায় না (প্রাথমিক callback, ছবি বাক্সের নিচে লুকানো) আর স্ক্রলের পরে Intersect: দেখা যাচ্ছে — এবার লোড. root হিসেবে scroll-বাক্সটি দেওয়া, তাই ফল আপনার পর্দার আকারের ওপর নির্ভর করে না। একটি সূক্ষ্ম বিষয়: কার্ডের height নির্দিষ্ট রাখা হয়েছে। Callback-এর class বদলে কার্ডের আকার আবার বদলালে Edge-এ “ResizeObserver loop completed with undelivered notifications.” error আসে (পরীক্ষা করে দেখা)। নিয়ম: ResizeObserver-এর callback-এ যে element দেখছেন, তার আকার বদলাবেন না।
আরেকটি observer-এর নাম জেনে রাখুন: PerformanceObserver. এটি পাতার পারফরম্যান্সের ঘটনা — লম্বা task, LCP, layout shift, নেটওয়ার্ক request-এর সময় — ঘটার সাথে সাথে জানায়; Core Web Vitals মাপার লাইব্রেরিগুলো এর ওপরই তৈরি। বিস্তারিত পর্ব ৫৩-এ। চারটি observer-এর নকশা একই — new XObserver(callback, options), observe(), unobserve(), disconnect() — এটি সফটওয়্যারের পুরোনো observer pattern-এর ব্রাউজার রূপ। কাজ শেষ হলে disconnect() ডাকতে ভুলবেন না, বিশেষত React-এর মতো framework-এ component সরানোর সময় — নইলে memory leak.
৯ · Feature detection, Baseline আর গোপনীয়তা — দায়িত্বশীল ব্যবহার
Feature detection বনাম browser sniffing
“এই ব্রাউজারে কি share আছে?” জানার দুটি উপায়। ভুল উপায় browser sniffing: navigator.userAgent string পড়ে ব্রাউজারের নাম আর version অনুমান করা — “Safari হলে এটা, Chrome হলে ওটা”. এটি ভঙ্গুর: প্রায় সব ব্রাউজারের string-এ আজও “Mozilla/5.0” লেখা, Edge-এর string-এ “Chrome” আছে, আর ব্রাউজারগুলো ইচ্ছা করে string-এর তথ্য কমাচ্ছে। নতুন version-এ API এলেও আপনার কোড জানবে না। ঠিক উপায় feature detection: সরাসরি দেখুন জিনিসটি আছে কি না।
// ✘ sniffing — ভঙ্গুর, ভুল ব্রাউজারকে বাদ দেয়
if (/Android|iPhone/.test(navigator.userAgent)) showShareButton();
// ✔ feature detection — ক্ষমতা দেখে সিদ্ধান্ত
if ("share" in navigator) showShareButton();
if ("IntersectionObserver" in window) lazyLoadWithObserver();
else loadAllImagesNow(); // পুরোনো ব্রাউজার: সব ছবি এখনই — কাজ তবু চলে
if (CSS.supports("container-type: inline-size")) useContainerQueries();শেষ else লাইনটিই progressive enhancement-এর মূল: API না থাকলে অভিজ্ঞতা একটু কম সুন্দর, কিন্তু ভাঙা নয়। কখনো কখনো polyfill দিয়ে অনুপস্থিত API যোগ করা যায় (IntersectionObserver-এর polyfill একসময় খুব প্রচলিত ছিল), কিন্তু অনুমতি-নির্ভর বা যন্ত্র-নির্ভর API — অবস্থান, vibration — polyfill করা যায় না; সেখানে শুধু বিকল্প পথ। CSS-এও একই নীতি: @supports.
Baseline — কোন API এখন নিশ্চিন্তে ব্যবহার করা যায়
২০২৩ সালে ব্রাউজার নির্মাতাদের একটি যৌথ দল (W3C-র WebDX Community Group) Baseline নামে একটি সহজ লেবেল চালু করে, যা এখন MDN আর web.dev-এ দেখা যায়। তিনটি অবস্থা: Limited availability — সব প্রধান ব্রাউজারে এখনো নেই; Newly available — Chrome, Edge, Firefox আর Safari-র (ডেস্কটপ ও মোবাইল) সর্বশেষ version-এ এসেছে; Widely available — তার প্রায় ৩০ মাস পরে। তিনটি observer অনেক আগেই widely available; Web Share বা Vibration এখনো সব ব্রাউজারে নেই। তবে Baseline একটি সংক্ষেপ, নিখুঁত সত্য নয় — বাংলাদেশে অনেকে পুরোনো Android ফোনে আপডেট-না-করা ব্রাউজার চালান। তাই feature detection কখনো বাদ দেবেন না।
গোপনীয়তা আর fingerprinting
প্রতিটি API একটি প্রশ্নের উত্তর দেয়: পর্দার আকার, ভাষা, font, CPU core, ব্যাটারি… আলাদাভাবে নিরীহ, কিন্তু একসাথে জুড়লে প্রায়ই একজন ব্যবহারকারীকে অনন্যভাবে চিনিয়ে দেয় — cookie ছাড়াই, আর cookie মুছলেও যা মোছে না। একে বলে browser fingerprinting. বিখ্যাত উদাহরণ Battery Status API: ২০১৫ সালের একটি গবেষণা দেখায়, ব্যাটারির সংখ্যাগুলো দিয়ে অল্প সময়ের জন্য ব্যবহারকারী শনাক্ত করা যায়; পরে Firefox API-টি সরিয়ে দেয়।
প্রতিরক্ষামূলক নিয়ম সহজ: শুধু সত্যিই দরকারি API ব্যবহার করুন; পাওয়া তথ্য যতটা সম্ভব ব্রাউজারেই রাখুন; আর মনে রাখুন, তৃতীয় পক্ষের script (বিজ্ঞাপন, analytics) আপনার পাতার সব অনুমতি পায়। Permissions Policy header দিয়ে অপ্রয়োজনীয় API বন্ধ রাখা যায় — পর্ব ৪৩-এর নিরাপত্তা-স্তরের অংশ।
একটি C, Python বা Java ডেস্কটপ প্রোগ্রাম সরাসরি অপারেটিং সিস্টেমের API ডাকে, আর ব্যবহারকারীর সব অধিকার নিয়ে চলে — যেকোনো ফাইল পড়তে পারে (Python-এর ফাইল I/O, Java-র NIO), কোনো prompt ছাড়াই। পার্থক্যটি ভাষায় নয়, sandbox-এ: ব্রাউজারের কোড যেকোনো পাতা খুললেই চলে, তাই তাকে প্রতিটি ক্ষমতা চেয়ে নিতে হয়। মোবাইল অ্যাপ এর মাঝামাঝি — Android 6.0 (২০১৫) থেকে অ্যাপকে অবস্থান বা ক্যামেরার মতো অনুমতি চালানোর সময় চাইতে হয়, ঠিক ব্রাউজারের মতো। আর observer API গুলো চিনতে পারবেন Java-র design pattern-এর Observer থেকে।
- Secure context কী, আর কেন লাগে? — https বা localhost থেকে আসা পাতা; পথে কেউ কোড বদলাতে পারে না বলে শুধু এখানেই geolocation, clipboard, notification-এর মতো শক্তিশালী API দেওয়া হয়।
- Lazy loading-এ IntersectionObserver কেন scroll listener-এর চেয়ে ভালো? — scroll-এ বারবার কোড চলে আর layout মাপতে হয়; observer-এ ব্রাউজার নিজে হিসাব করে শুধু সীমা পার হলে callback দেয়। শুধু ছবির জন্য
loading="lazy"-ই যথেষ্ট। - MutationObserver-এর callback কখন চলে? — বর্তমান synchronous কোড শেষ হলে একটি microtask-এ, একসাথে জমা সব record নিয়ে — প্রতিটি বদলে আলাদা করে নয়।
- Feature detection আর browser sniffing-এর পার্থক্য? — প্রথমটি সরাসরি দেখে API আছে কি না (
'share' in navigator); দ্বিতীয়টি userAgent থেকে ব্রাউজার অনুমান করে — ভঙ্গুর ও ভুলপ্রবণ। - Notification আর push notification-এর পার্থক্য? — প্রথমটি খোলা পাতা থেকে দেখানো হয়; দ্বিতীয়টি সার্ভার পাঠায়, পাতা বন্ধ থাকলেও service worker সেটি দেখায়।
১০ · AI-এর যুগে — AI-এর লেখা Web API কোড যাচাই করা
“একটি copy button বানিয়ে দাও” বললে AI কয়েক সেকেন্ডে কোড দেয়, আর আপনার localhost-এ, Allow চাপা অবস্থায় সেটি নিখুঁত চলে। সমস্যা দেখা দেয় আসল ব্যবহারকারীর কাছে — যিনি Block চেপেছেন বা পুরোনো ব্রাউজার চালান। পাঁচটি ভুল বিশেষভাবে দেখুন।
| AI-এর সাধারণ ভুল | ফল | ঠিক রূপ |
|---|---|---|
| পাতা খোলামাত্র অবস্থান বা notification-এর অনুমতি চাওয়া | বেশিরভাগ ব্যবহারকারী Block চাপেন, আর সেই সিদ্ধান্ত স্থায়ী; ব্রাউজার “শান্ত” UI দিয়ে prompt লুকিয়েও ফেলতে পারে | ব্যবহারকারীর ক্লিকের উত্তরে, আগে এক লাইনে কারণ বুঝিয়ে |
error callback বা catch নেই; denied ধরে নেওয়া হয়নি | Block করলে বোতাম চুপচাপ কিছুই করে না, spinner ঘুরতেই থাকে | প্রতিটি অনুমতি-নির্ভর কাজে ব্যর্থতার পথ আর বিকল্প (এলাকা বাছাই, লিংক সিলেক্ট) |
Lazy loading-এ scroll listener আর getBoundingClientRect | স্ক্রলে ঝাঁকুনি (jank), ফোনে ব্যাটারি খরচ | loading="lazy", বা IntersectionObserver + rootMargin + unobserve |
শুধু document.execCommand('copy'), ফলাফল যাচাই ছাড়া | deprecated পথ; ব্যর্থ হলেও “কপি হয়েছে” দেখায় | navigator.clipboard.writeText + try/catch + সিলেক্ট-করা fallback |
navigator.userAgent দেখে “iPhone হলে share বোতাম” | নতুন ব্রাউজার বা ডেস্কটপের share বাদ পড়ে; ভুল ব্রাউজারে ভাঙা বোতাম | 'share' in navigator / navigator.canShare(data) |
AI-কে এমন কোড লেখাতে চাইলে শর্তগুলো আগেই বলে দিন — আর ব্যর্থতার পরীক্ষাও চেয়ে নিন:
AI-এর লেখা Web API কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১একসময় ওয়েবে ভিডিও, গেম বা ক্যামেরার জন্য Flash বা Java applet-এর মতো plugin লাগত। Steve Jobs-এর ২০১০ সালের “Thoughts on Flash” থেকে আজকের standard Web API — এই বদল কেন ঘটল, আর এতে কী লাভ ও কী ক্ষতি হলো?
১৯৯০-এর দশকের শেষ থেকে প্রায় পনেরো বছর ব্রাউজার নিজে খুব কম কিছু পারত — লেখা, ছবি, ফর্ম। ভিডিও, অ্যানিমেশন, গেম, ক্যামেরা বা মাইক্রোফোন চাইলে লাগত plugin: Macromedia (পরে Adobe) Flash, Sun-এর Java applet, Microsoft-এর ActiveX আর পরে Silverlight. এগুলো ব্রাউজারের sandbox-এর বাইরে, প্রায় একটি আলাদা প্রোগ্রামের মতো চলত, ব্যবহারকারীর কম্পিউটারে অনেক বেশি অধিকার নিয়ে। ফলে বছরের পর বছর Flash আর Java ছিল আক্রমণকারীদের প্রিয় দরজা — নিরাপত্তা-হালনাগাদের তালিকায় এদের নাম লেগেই থাকত।
২০০৭ সালে প্রথম iPhone Flash ছাড়াই আসে। ২০১০ সালের এপ্রিলে Steve Jobs Apple-এর ওয়েবসাইটে একটি খোলা চিঠি প্রকাশ করেন, “Thoughts on Flash”. তিনি যুক্তি দেন: Flash একটি একক কোম্পানির বন্ধ পণ্য; Mac-এ crash-এর বড় কারণ; ভিডিও চালাতে হার্ডওয়্যারের সাহায্য না নিয়ে ব্যাটারি খায়; আর মাউসের জন্য তৈরি, আঙুলের স্পর্শের জন্য নয়। বিকল্প হিসেবে তিনি খোলা মানদণ্ডের কথা বলেন — HTML5, CSS, JavaScript. চিঠিটি বিতর্কিত ছিল (অনেকে বলেন Apple আসলে নিজের App Store-এর নিয়ন্ত্রণ রক্ষা করছিল), কিন্তু ফল স্পষ্ট: ২০১১ সালে Adobe মোবাইলের জন্য Flash বানানো বন্ধ করে, Chrome ২০১৫ সালে পুরোনো NPAPI plugin ব্যবস্থা সরিয়ে দেয়, Java 9-এ applet deprecated হয়, আর ২০২০ সালের ৩১ ডিসেম্বর Flash-এর আনুষ্ঠানিক জীবন শেষ হয়।
শূন্যস্থান পূরণ করে standard Web API: <video>, Canvas, WebGL, WebRTC (ক্যামেরা), Geolocation, Notification. লাভ বিশাল — প্রতিটি API এখন খোলা মানদণ্ডে লেখা, একাধিক ব্রাউজার আলাদাভাবে বাস্তবায়ন করে, আর সবচেয়ে বড় কথা, সবই ব্রাউজারের sandbox আর permission ব্যবস্থার ভেতরে। ক্ষতিও আছে: plugin-এর যুগে একটি নতুন ক্ষমতা একটি কোম্পানির সিদ্ধান্তেই রাতারাতি আসত; এখন প্রতিটি API-কে মানদণ্ড সংস্থা আর ব্রাউজার নির্মাতাদের ঐকমত্যের ধীর পথ পেরোতে হয়। Flash-এ বানানো অসংখ্য শিক্ষামূলক গেম ও অ্যানিমেশন হারিয়ে যাওয়ার ঝুঁকিতে পড়ে — Internet Archive-এর মতো প্রতিষ্ঠান emulator দিয়ে সেগুলো বাঁচানোর চেষ্টা করছে।
মূল উপলব্ধি: ক্ষমতা দেওয়ার দুই দর্শন — “সব অধিকার দিয়ে plugin” আর “ধাপে ধাপে, অনুমতি নিয়ে standard API”. ওয়েব দ্বিতীয়টি বেছেছে; এই পর্বের প্রতিটি permission prompt আর feature detection সেই সিদ্ধান্তেরই দৈনন্দিন রূপ।
প্র ০২W3C-র Geolocation API তৈরির সময় (২০০৮–২০১৩) গোপনীয়তা নিয়ে কী বিতর্ক হয়েছিল? শুধু “Allow/Block” বাক্স কি গোপনীয়তা রক্ষার জন্য যথেষ্ট?
২০০৮ সালে W3C একটি Geolocation Working Group গঠন করে। API-র নকশা অনেকটাই এসেছিল Google Gears নামের একটি ব্রাউজার-এক্সটেনশনের অভিজ্ঞতা থেকে, আর লক্ষ্য ছিল সরল: একটি পাতা getCurrentPosition ডাকবে, ব্রাউজার ব্যবহারকারীকে জিজ্ঞেস করবে, তারপর অক্ষাংশ-দ্রাঘিমাংশ দেবে। ২০০৯ সালে Firefox 3.5 প্রথম বড় ব্রাউজার হিসেবে এটি চালু করে; ২০১৩ সালে API-টি W3C Recommendation হয়।
বিতর্ক ওঠে অন্য একটি মানদণ্ড সংস্থার দিক থেকে। ইন্টারনেটের মানদণ্ড সংস্থা IETF-এর Geopriv নামের একটি দল বহু বছর ধরে অবস্থান-তথ্যের গোপনীয়তা নিয়ে কাজ করছিল। তাদের প্রস্তাব ছিল: অবস্থানের সাথে গোপনীয়তার নিয়মও জুড়ে দেওয়া হোক — “এই তথ্য কতদিন রাখা যাবে”, “অন্য কাউকে দেওয়া যাবে কি না” — যাতে তথ্য যেখানেই যাক, নিয়ম সাথে যায়। W3C-র দল এটি গ্রহণ করেনি। তাদের যুক্তি ছিল: একটি ওয়েবসাইট এই নিয়ম মানছে কি না, ব্রাউজার কোনোভাবে যাচাই করতে পারবে না; একটি অকার্যকর নিয়ম মিথ্যা নিরাপত্তার অনুভূতি দেবে। শেষে spec-এ শুধু লেখা থাকে যে ব্রাউজারকে অবশ্যই ব্যবহারকারীর স্পষ্ট অনুমতি নিতে হবে, আর ওয়েবসাইটের জন্য কিছু “উচিত” ধরনের সুপারিশ — তথ্য শুধু দরকারি কাজে ব্যবহার, দরকার শেষে মুছে ফেলা, ব্যবহারকারীকে জানানো। ২০১০ সালে UC Berkeley-র গবেষক Nick Doty, Deirdre Mulligan আর Erik Wilde এই প্রক্রিয়া নিয়ে একটি বিশ্লেষণ প্রকাশ করেন, আর দেখান যে অনুমতি নেওয়ার পরে তথ্যের কী হয়, সেই প্রশ্নটি কার্যত ওয়েবসাইটের সততার হাতে ছেড়ে দেওয়া হয়েছে।
আজও ছবিটি একই। “Allow” চাপার মুহূর্তে ব্যবহারকারী জানেন না অবস্থানটি একবার দূরত্ব হিসাবে লাগবে, না চিরকাল সার্ভারে জমা থাকবে, না বিজ্ঞাপনদাতার কাছে বিক্রি হবে। ব্রাউজার যা পারে, তা করেছে: শুধু https-এ API, প্রতিটি সাইটের জন্য আলাদা অনুমতি, ঠিকানা-বারে অবস্থান-চিহ্ন, কিছু অপারেটিং সিস্টেমে “আনুমানিক অবস্থান” দেওয়ার বিকল্প। বাকিটা আইন (যেমন ইউরোপের GDPR) আর ডেভেলপারের নৈতিকতা।
মূল উপলব্ধি: অনুমতি নেওয়া গোপনীয়তার শুরু, শেষ নয়। প্রযুক্তি শুধু তথ্যে প্রবেশ নিয়ন্ত্রণ করতে পারে; তথ্য পাওয়ার পরে কী হবে, সেটি আপনার কোড আর আপনার সিদ্ধান্ত।
প্র ০৩Google-এর Project Fugu ওয়েবকে USB, Bluetooth, ফাইল সিস্টেমের মতো যন্ত্র-ক্ষমতা দিতে চায়, কিন্তু Mozilla আর Apple এর অনেকগুলোর বিরোধিতা করে। কে ঠিক — আর ওয়েব কতটা ক্ষমতা পাওয়া উচিত?
২০১৮ সালের দিকে Google, Microsoft, Intel-সহ কয়েকটি কোম্পানি Chromium-এ একটি উদ্যোগ শুরু করে, যার অনানুষ্ঠানিক নাম Project Fugu — জাপানের পটকা মাছ, যা ঠিকভাবে রাঁধলে সুস্বাদু, ভুলভাবে রাঁধলে বিষাক্ত। নামটিই বলে দেয় দলটি ঝুঁকি জানত। লক্ষ্য: নেটিভ অ্যাপ যা পারে আর ওয়েব যা পারে, তার ফাঁক কমানো। এর ফলে Chrome-এ এসেছে WebUSB, Web Bluetooth, Web Serial, WebHID, File System Access, Web NFC (Android) — একটি ওয়েব পাতা থেকে Arduino প্রোগ্রাম করা, একটি স্মার্টওয়াচ পড়া বা নিজের কম্পিউটারের ফোল্ডারে ফাইল সেভ করা সম্ভব হয়েছে।
Mozilla প্রকাশ্যে তার “standards positions” তালিকায় WebUSB, Web Bluetooth আর এমন কয়েকটি API-কে ক্ষতিকর (harmful) বলে চিহ্নিত করে। যুক্তি: এই যন্ত্রগুলো কখনো ওয়েবের অচেনা কোডের মুখোমুখি হওয়ার জন্য বানানো হয়নি; একটি USB যন্ত্রের firmware-এ দুর্বলতা থাকলে একটি ওয়েবসাইট সেটি কাজে লাগাতে পারে; আর একটি সাধারণ মানুষ “এই সাইট আপনার USB যন্ত্রে প্রবেশ চায়” বাক্সের ঝুঁকি বোঝার মতো অবস্থায় থাকেন না। ২০২০ সালে Apple-এর WebKit দল জানায়, তারা fingerprinting-এর ঝুঁকির কারণে Web Bluetooth, WebUSB, WebMIDI, Web NFC, Battery Status-সহ ১৬টি API বাস্তবায়ন করবে না।
Google-এর দিকের যুক্তিও শক্তিশালী: এই কাজগুলোর জন্য মানুষ এখন অচেনা নেটিভ অ্যাপ নামান, যেগুলোর কোনো sandbox নেই, কোনো যন্ত্র-ভিত্তিক অনুমতি নেই — ওয়েব API সেই তুলনায় অনেক নিরাপদ। Fugu-র API গুলো ব্যবহারকারীকে প্রতিটি যন্ত্র নিজে বেছে দিতে বাধ্য করে (পাতা সব USB যন্ত্রের তালিকা পায় না), শুধু https-এ চলে, আর user activation চায়। সমালোচকেরা আরও একটি দিক তোলেন: Apple-এর iPhone-এ সব ব্রাউজারকে Safari-র engine ব্যবহার করতে হতো, তাই Apple-এর “না” মানে iPhone-এ সেই API কোনো ব্রাউজারেই নেই — আর এতে ওয়েব অ্যাপের তুলনায় App Store-এর অ্যাপ সুবিধা পায়। এখানে নিরাপত্তা, গোপনীয়তা আর ব্যবসা জট পাকিয়ে আছে; কোনো পক্ষের যুক্তিই শুধু একটি কারণে নয়।
মূল উপলব্ধি: প্রতিটি নতুন Web API একটি বিনিময় — ক্ষমতা বনাম ঝুঁকি, সুবিধা বনাম fingerprinting. এজন্যই এই পর্বের নিয়ম চিরস্থায়ী: একটি API কোনো ব্রাউজারে থাকলেও অন্যটিতে না-ও থাকতে পারে; feature detection আর বিকল্প পথ ছাড়া কোড লিখবেন না।
প্র ০৪“Permission prompt fatigue” কী? ২০২০ সালে Chrome আর Firefox notification-এর অনুমতি চাওয়ার নিয়ম কেন বদলাল, আর ডেভেলপারদের জন্য এর শিক্ষা কী?
Notification API ব্রাউজারে আসার পরে কয়েক বছরের মধ্যে একটি অভ্যাস ছড়িয়ে পড়ে: যেকোনো সংবাদ সাইট, রেসিপি ব্লগ বা অনলাইন দোকান খুললেই প্রথম সেকেন্ডে “এই সাইট notification দেখাতে চায় — Allow / Block”. ব্যবহারকারী তখনো জানেনই না সাইটটি কী, এর notification কেমন হবে। ফল দুটি। প্রথমত, বেশিরভাগ মানুষ ভেবে না দেখে Block বা বাক্স বন্ধ করতে শুরু করেন — কারণ প্রতিদিন দশটি সাইট একই প্রশ্ন করে। এই অবস্থাকে বলে permission prompt fatigue: এত বেশি জিজ্ঞাসা যে মানুষ আর পড়েনই না। দ্বিতীয়ত, যাঁরা অভ্যাসবশে Allow চাপেন, তাঁরা পরে spam notification-এ ডুবে যান — কিছু প্রতারক সাইট notification দিয়েই ভুয়া “আপনার ফোনে ভাইরাস” বার্তা পাঠাত।
ব্রাউজার নির্মাতারা ডেটা দেখে সিদ্ধান্ত নেন। Mozilla জানায়, Firefox-এর notification prompt-এর বেশিরভাগই প্রত্যাখ্যাত বা উপেক্ষিত হয়। ২০২০ সালের শুরুতে Firefox 72 নিয়ম করে: ব্যবহারকারীর কোনো ক্লিক বা কী-প্রেস ছাড়া notification-এর prompt আর পপ-আপ হবে না; বদলে ঠিকানা-বারে একটি ছোট চিহ্ন দেখা যাবে। প্রায় একই সময়ে Chrome 80 একটি “quieter notification permission UI” আনে — যাঁরা প্রায়ই Block করেন, আর যে সাইটগুলোর prompt খুব কম মানুষ গ্রহণ করে, সেখানে পূর্ণ বাক্সের বদলে ঠিকানা-বারে একটি শান্ত ঘণ্টা-চিহ্ন। পরে Chrome অপব্যবহারকারী সাইটের notification স্বয়ংক্রিয়ভাবে শান্ত করার নিয়মও যোগ করে।
ডেভেলপারের জন্য শিক্ষা সরাসরি: অনুমতির prompt একটি সীমিত সম্পদ। ব্যবহারকারীর একবারের Block সাধারণত চিরস্থায়ী, আর ব্রাউজার খারাপ আচরণকারী সাইটকে শাস্তি দেয়। সফল নকশা দেখতে এমন: অর্ডার দেওয়ার পরে পাতায় একটি নিজস্ব বাক্স — “অর্ডারের অবস্থা notification-এ জানতে চান?” — ব্যবহারকারী “হ্যাঁ” চাপলে তবেই ব্রাউজারের prompt. এভাবে ব্রাউজারের prompt শুধু সেই মানুষের কাছে যায়, যিনি ইতিমধ্যে আগ্রহ দেখিয়েছেন — আর গ্রহণের হার নাটকীয়ভাবে বাড়ে। “না” চাপলে নিজের বাক্সের সিদ্ধান্ত মনে রাখুন, বারবার জিজ্ঞেস করবেন না।
মূল উপলব্ধি: ব্যবহারকারীর মনোযোগ আর বিশ্বাস সবচেয়ে দামি সম্পদ। প্রতিটি অপ্রাসঙ্গিক prompt শুধু আপনার সাইটের নয়, পুরো ওয়েবের বিশ্বাস কমায় — আর ব্রাউজারগুলো সেটি ঠেকাতে নিয়ম কঠোর করতেই থাকবে।
প্র ০৫IntersectionObserver আসার আগে lazy loading, infinite scroll আর বিজ্ঞাপনের “দেখা হয়েছে” মাপা কীভাবে হতো? একটি observer API কীভাবে ওয়েবের পারফরম্যান্সের ইতিহাস বদলাল?
২০১০-এর দশকের প্রথম ভাগে প্রায় প্রতিটি বড় সাইট একই কৌশল ব্যবহার করত: window-এ একটি scroll listener, আর তার ভেতরে প্রতিটি ছবি বা বিজ্ঞাপনের getBoundingClientRect() বা offsetTop পড়া। jQuery-র জনপ্রিয় lazyload plugin থেকে শুরু করে বিজ্ঞাপন নেটওয়ার্কের script — সবাই তা-ই করত। সমস্যা হলো, এমন প্রতিটি মাপ ব্রাউজারকে layout সর্বশেষ অবস্থায় আনতে বাধ্য করে; আর scroll event-এর মাঝে DOM লিখলে-পড়লে বারবার layout হয় (layout thrashing). একটি পাতায় দশটি আলাদা script, প্রত্যেকে নিজের scroll listener নিয়ে — ফোনে স্ক্রল ঝাঁকুনি দিত, ব্যাটারি গলত।
বিজ্ঞাপনের দিকটি আরও জটিল ছিল। বিজ্ঞাপন শিল্পের নিয়ম অনুযায়ী (Media Rating Council-এর মানদণ্ড), একটি display বিজ্ঞাপন “দেখা হয়েছে” গোনা হয় যখন এর অন্তত অর্ধেক pixel টানা অন্তত এক সেকেন্ড পর্দায় থাকে। বিজ্ঞাপন প্রায়ই অন্য origin-এর iframe-এ থাকে, আর সেই iframe-এর ভেতরের কোড বাইরের পাতার scroll অবস্থান জানতে পারে না। তাই বিজ্ঞাপনদাতারা অদ্ভুত কৌশল নিত — যেমন ছোট ছোট Flash বস্তু বসিয়ে ব্রাউজার কোনটিকে কম গতিতে আঁকছে তা মেপে অনুমান করা যে বিজ্ঞাপনটি পর্দার বাইরে কি না।
Google-এর Chrome দলের প্রকৌশলীরা এই অবস্থা থেকে IntersectionObserver প্রস্তাব করেন। মূল উপলব্ধি ছিল: ব্রাউজার প্রতিটি frame আঁকার সময় এমনিতেই জানে কোন element কোথায় — সেই তথ্য JavaScript-কে asynchronous-ভাবে, শুধু দরকারের মুহূর্তে দিলেই হয়। Chrome ২০১৬ সালে (Chrome 51) এটি চালু করে; Edge আর Firefox পরের বছরে, আর Safari ২০১৯ সালে যোগ দেওয়ার পরে এটি সব প্রধান ব্রাউজারে আসে। আরও একটি বড় সুবিধা: এটি অন্য origin-এর iframe-এর ভেতরেও কাজ করে, তাই বিজ্ঞাপনের viewability মাপা অনেক সৎ আর সস্তা হয়। এর ভিত্তিতে আরও এগিয়ে ২০১৯ সালে Chrome ছবির জন্য নেটিভ loading="lazy" আনে, পরে অন্য ব্রাউজারও — অনেক ক্ষেত্রে আর এক লাইন JavaScript-ও লাগে না।
একই দর্শন পরের API গুলোতেও: ResizeObserver (window-এর resize-এর জায়গায়), MutationObserver (synchronous Mutation Events-এর জায়গায়), PerformanceObserver. সবগুলোর সাধারণ সূত্র — “বারবার জিজ্ঞেস করো না, ব্রাউজারকে বলো খবর দিতে; আর খবর দাও জমিয়ে, asynchronous-ভাবে।”
মূল উপলব্ধি: পারফরম্যান্সের অনেক বড় উন্নতি আসে ভালো API নকশা থেকে, দ্রুত কোড থেকে নয়। যে তথ্য ব্রাউজারের কাছে আগেই আছে, তা আপনার কোড দিয়ে আবার হিসাব করাই আসল অপচয়।
অনুশীলন
প্রথম তিনটিতে কোড লিখতে হবে না — ভাবুন, কাগজে লিখুন, তারপর উত্তর মিলিয়ে নিন।
-
অনুমতির অডিট: একটি খাবার-ডেলিভারি সাইট পাতা খোলামাত্র তিনটি prompt দেখায় — অবস্থান, notification আর ক্যামেরা (“প্রোফাইল ছবি তুলতে”)। প্রতিটির জন্য বলুন কখন চাওয়া উচিত, আর ব্যবহারকারী Block করলে কী বিকল্প দেবেন।
অবস্থান: ঠিকানা দেওয়ার ধাপে, “আমার বর্তমান অবস্থান ব্যবহার করুন” বোতামে ক্লিক করলে। Block হলে ঠিকানা লেখার ঘর আর এলাকার তালিকা — মূল কাজ তাতেই চলে। Notification: অর্ডার দেওয়ার পরে, পাতার নিজের বাক্সে “অর্ডারের খবর notification-এ চান?” — হ্যাঁ বললে তবেই ব্রাউজারের prompt. Block হলে অর্ডার-ট্র্যাকিং পাতা আর SMS. ক্যামেরা: শুধু প্রোফাইল পাতায় “ছবি তুলুন” চাপলে; সাথে “গ্যালারি থেকে বাছুন” (
<input type="file" accept="image/*">), যার জন্য কোনো অনুমতিই লাগে না। মূলনীতি: পাতা খোলামাত্র একটিও নয়, আর কোনো অনুমতি না দিলেও খাবার অর্ডার করা যায়। -
আউটপুট অনুমান করুন: একটি
ul-এnew MutationObserver(r => console.log("records:", r.length)).observe(ul, { childList: true }). তারপর একই synchronous কোডে: একটিforলুপে ৫টিliappend, তারপরul.className = "done", তারপরconsole.log("শেষ"), তারপরawait Promise.resolve()-এর পরে আরও একটিliappend. কী ছাপা হবে, কোন ক্রমে?প্রথমে
শেষ. তারপরrecords: 5— পাঁচটি append একটি microtask-এ একসাথে;classNameবদল ধরা পড়েনি, কারণattributes: trueদেওয়া নেই। তারপরrecords: 1—await-এর পরের append একটি নতুন microtask-এ নতুন batch তৈরি করে। (যেকোনো async function-এ এই কোড চালিয়ে মিলিয়ে নিন।) সূক্ষ্ম বিষয়: observer-এর microtask আরawait-এর microtask একই queue-তে, তাই আগে যেটি queue-তে ঢুকেছে সেটি আগে চলে। -
নকশা করুন: একটি লাইভ ক্রিকেট স্কোরের পাতা প্রতি ১০ সেকেন্ডে স্কোর আনে, আর নতুন উইকেট পড়লে ছোট একটি শব্দ বাজায়। ব্যবহারকারী অন্য ট্যাবে গেলে কী কী বদলানো উচিত? ফিরে এলে কী করবেন?
লুকালে (
visibilitychange→hidden): polling বন্ধ বা অনেক ধীর (ধরা যাক প্রতি ১ মিনিটে), অ্যানিমেশন বন্ধ, শব্দ বন্ধ (কেউ দেখছেন না এমন ট্যাব থেকে হঠাৎ শব্দ বিরক্তিকর)। ব্যবহারকারী চাইলে আগে অনুমতি নিয়ে উইকেটের notification দেওয়া যায় — সেটিই পেছনের ট্যাব থেকে খবর দেওয়ার সঠিক পথ। ফিরলে (visible): সাথে সাথে একবার স্কোর আনুন (পুরোনো স্কোর দেখাবেন না), তারপর আবার প্রতি ১০ সেকেন্ডে; মাঝের উইকেটগুলো একটি সংক্ষেপে দেখান (“আপনি না থাকাকালীন ২টি উইকেট”)। আরও ভালো হলে polling-এর বদলে পর্ব ৫১-এর WebSocket বা Server-Sent Events. -
কোড লিখুন — infinite scroll: একটি পণ্য-তালিকা
#listআর তার নিচে<div id="sentinel">.loadPage(n)একটি promise দেয়, যা পণ্যের array বা শেষ পাতায় খালি array দেয়। IntersectionObserver দিয়ে লিখুন: sentinel দেখা দিলে পরের পাতা আনুন, একসাথে দুবার নয়, আর পণ্য শেষ হলে নজর বন্ধ করুন।একটি সম্ভাব্য সমাধান:
let page = 1, loading = false; const io = new IntersectionObserver(async ([entry]) => { if (!entry.isIntersecting || loading) return; // দুবার নয় loading = true; try { const items = await loadPage(page++); if (items.length === 0) { io.disconnect(); return; } // সব শেষ list.append(...items.map(renderCard)); } catch { page--; // পরের বার আবার চেষ্টা } finally { loading = false; } }, { rootMargin: "400px 0px" }); io.observe(document.querySelector("#sentinel"));loadingflag না থাকলে একই পাতা দুবার আসতে পারে। সূক্ষ্ম সমস্যা: নতুন পণ্য যোগের পরেও sentinel পর্দার ভেতরে থাকলে (খুব বড় পর্দা) নতুন callback আসে না, কারণ অবস্থা বদলায়নি। সমাধান:finally-তেio.unobserve(sentinel); io.observe(sentinel);করে আবার যাচাই করানো। আর একটি “আরও দেখুন” বোতামও রাখুন — কিবোর্ড ব্যবহারকারীদের জন্য।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি ওয়েব পাতায় “কাছের পিকআপ পয়েন্ট খুঁজুন” বৈশিষ্ট্যের সঠিক ধাপগুলো সাজান (▲▼ চেপে বা টেনে)।
- 🔎 'geolocation' in navigator দিয়ে API আছে কি না দেখা
- 💬 পাতায় এক লাইনে কারণ বোঝানো, বোতাম দেখানো
- 👆 ব্যবহারকারী বোতামে ক্লিক করলেন
- ❓ getCurrentPosition ডাকা — ব্রাউজারের prompt
- ⚖️ Allow হলে অবস্থান, Block হলে error code 1
- 📍 haversine দিয়ে নিকটতম পয়েন্ট, অথবা এলাকা বাছাইয়ের বিকল্প
নিচের কোন ঠিকানা থেকে খোলা পাতায় window.isSecureContext হবে false?
- https://shop.example.com
- http://localhost:5173
- http://192.168.0.5:5173
- http://127.0.0.1:8080
JavaScript-এ navigator.permissions.query()-এর ফলাফলের state কোনটি হতে পারে না?
- "granted"
- "denied"
- "prompt"
- "blocked"
একটি MutationObserver একটি element দেখছে। একই synchronous কোডে সেই element-এ তিনটি child যোগ করা হলো। callback কতবার আর কখন চলবে?
- তিনবার, প্রতিটি যোগ করার সাথে সাথে
- একবার, কোড শেষ হওয়ার পরে একটি microtask-এ, তিনটি record সহ
- একবার, পরের frame আঁকার সময়
- একবারও না, কারণ পরিবর্তনগুলো একই কোডে হয়েছে
Lazy loading-এর জন্য scroll event-এ প্রতিটি ছবির getBoundingClientRect() যাচাই করার মূল সমস্যা কী?
- ঘন ঘন main thread-এ কোড চলে আর layout হিসাব করতে হয়, ফলে স্ক্রল ধীর হয়
- getBoundingClientRect পুরোনো ব্রাউজারে নেই
- scroll event-এর জন্য ব্যবহারকারীর অনুমতি লাগে
- getBoundingClientRect সবসময় ভুল অবস্থান দেয়
Geolocation API-র getCurrentPosition-এর error callback-এ err.code === 1 মানে কী?
- সময় শেষ হয়ে গেছে
- যন্ত্র অবস্থান বের করতে পারেনি
- অনুমতি দেওয়া হয়নি (PERMISSION_DENIED)
- অবস্থান সফলভাবে পাওয়া গেছে
একটি পাতায় “শেয়ার” বোতাম শুধু সেই ব্রাউজারে দেখাতে চান, যেখানে Web Share API আছে। কোন পদ্ধতিটি সবচেয়ে নির্ভরযোগ্য?
- navigator.userAgent-এ "Android" বা "iPhone" আছে কি না দেখা
- পর্দার প্রস্থ ৭৬৮px-এর কম কি না দেখা
- পাতা খোলার সময় একবার navigator.share() ডেকে দেখা
- 'share' in navigator বা navigator.canShare(data) দিয়ে যাচাই
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখতে পাবেন।
মনে রাখুন
- শক্তিশালী API-র তিন দরজা: secure context (https বা localhost — LAN-এর http নয়), user activation (ক্লিকের ভেতরে, আগে লম্বা
awaitনয়), আর অনুমতি (granted/denied/prompt). - প্রসঙ্গে চান, আগে বোঝান, কম চান — পাতা খোলামাত্র কখনো নয়। Block একবার হলে সাধারণত স্থায়ী।
- প্রতিটি API-র আগে feature detection, প্রতিটি অনুমতির পরে fallback: লিংক সিলেক্ট করে দেখানো, এলাকা বাছাই, পাতার ভেতরের ব্যানার। userAgent sniffing নয়।
- Polling-এর বদলে observer: IntersectionObserver (lazy load, infinite scroll, seen), ResizeObserver (element-এর আকার), MutationObserver (DOM বদল, microtask-এ জমিয়ে) — কাজ শেষে
unobserve/disconnect. শুধু ছবিতেloading="lazy". - লুকানো ট্যাবে কাজ থামান (
visibilitychange), আর ব্যক্তিগত তথ্য যতটা সম্ভব ব্রাউজারেই রাখুন — প্রতিটি API fingerprinting-এর পথও হতে পারে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৪২ · Web Worker — main thread খালি রাখাপরবর্তী পর্বভারী কাজ আলাদা thread-এ, পাতা থাকে মসৃণ।
- JavaScript প্রোগ্রামিং · কোর্স হোম৫৯টি পর্বপুরো কোর্সের রোডম্যাপ — কোন পর্ব কখন।
- পর্ব ৩৬ · AbortController, retry, debounce আর throttleফিরে দেখুনAbortError, polling আর throttle — এই পর্বের API গুলোর সঙ্গী নকশা।
- Mobile App Developmentপাশের ট্র্যাকনেটিভ অ্যাপ কীভাবে অনুমতি চায় আর যন্ত্রের ক্ষমতা ব্যবহার করে।
- Human-Computer Interactionগভীরে যানভালো permission UX-এর পেছনের মানুষ-কেন্দ্রিক নকশার নীতি।