একটি নতুন API-এর মাধ্যমে ক্লায়েন্ট-সাইড রাউটিংকে মানসম্মত করা যা একক-পৃষ্ঠার অ্যাপ্লিকেশন তৈরির সম্পূর্ণরূপে সংস্কার করে।
একক-পৃষ্ঠার অ্যাপ্লিকেশন, বা SPA, একটি মূল বৈশিষ্ট্য দ্বারা সংজ্ঞায়িত করা হয়: ব্যবহারকারী সাইটের সাথে ইন্টারঅ্যাক্ট করার সাথে সাথে তাদের বিষয়বস্তু গতিশীলভাবে পুনর্লিখন করা, সার্ভার থেকে সম্পূর্ণ নতুন পৃষ্ঠা লোড করার ডিফল্ট পদ্ধতির পরিবর্তে।
যদিও SPA গুলি History API এর মাধ্যমে (অথবা সীমিত ক্ষেত্রে, সাইটের #hash অংশটি সামঞ্জস্য করে) এই বৈশিষ্ট্যটি আপনার কাছে আনতে সক্ষম হয়েছে, এটি একটি জটিল API যা SPA গুলি আদর্শ হওয়ার অনেক আগে থেকেই তৈরি করা হয়েছিল - এবং ওয়েব সম্পূর্ণ নতুন পদ্ধতির জন্য চিৎকার করছে। Navigation API হল একটি প্রস্তাবিত API যা এই স্থানটিকে সম্পূর্ণরূপে ওভারহল করে, কেবল History API এর রুক্ষ প্রান্তগুলি প্যাচ করার চেষ্টা করার পরিবর্তে। (উদাহরণস্বরূপ, Scroll Restoration History API পুনরায় উদ্ভাবনের চেষ্টা করার পরিবর্তে প্যাচ করেছে।)
এই পোস্টে নেভিগেশন API-এর উচ্চ স্তরের বর্ণনা দেওয়া হয়েছে। প্রযুক্তিগত প্রস্তাবটি পড়তে, WICG সংগ্রহস্থলে খসড়া প্রতিবেদনটি দেখুন।
ব্যবহারের উদাহরণ
নেভিগেশন API ব্যবহার করার জন্য, গ্লোবাল navigation অবজেক্টে একটি "navigate" লিসেনার যোগ করে শুরু করুন। এই ইভেন্টটি মূলত কেন্দ্রীভূত : এটি সকল ধরণের নেভিগেশনের জন্য কার্যকর হবে, ব্যবহারকারী কোনও ক্রিয়া সম্পাদন করেছেন (যেমন কোনও লিঙ্কে ক্লিক করা, কোনও ফর্ম জমা দেওয়া, বা পিছনে এবং এগিয়ে যাওয়া) অথবা যখন নেভিগেশন প্রোগ্রাম্যাটিকভাবে ট্রিগার করা হয় (অর্থাৎ, আপনার সাইটের কোডের মাধ্যমে)। বেশিরভাগ ক্ষেত্রে, এটি আপনার কোডকে সেই ক্রিয়াটির জন্য ব্রাউজারের ডিফল্ট আচরণকে ওভাররাইড করতে দেয়। SPA-এর জন্য, এর অর্থ সম্ভবত ব্যবহারকারীকে একই পৃষ্ঠায় রাখা এবং সাইটের সামগ্রী লোড করা বা পরিবর্তন করা।
একটি NavigateEvent "navigate" শ্রোতার কাছে পাঠানো হয় যাতে নেভিগেশন সম্পর্কিত তথ্য থাকে, যেমন গন্তব্য URL, এবং আপনাকে একটি কেন্দ্রীভূত স্থানে নেভিগেশনে সাড়া দেওয়ার অনুমতি দেয়। একটি মৌলিক "navigate" শ্রোতা দেখতে এরকম হতে পারে:
navigation.addEventListener('navigate', navigateEvent => {
// Exit early if this navigation shouldn't be intercepted.
// The properties to look at are discussed later in the article.
if (shouldNotIntercept(navigateEvent)) return;
const url = new URL(navigateEvent.destination.url);
if (url.pathname === '/') {
navigateEvent.intercept({handler: loadIndexPage});
} else if (url.pathname === '/cats/') {
navigateEvent.intercept({handler: loadCatsPage});
}
});
আপনি দুটি উপায়ের একটিতে নেভিগেশন মোকাবেলা করতে পারেন:
- নেভিগেশন পরিচালনা করার জন্য
intercept({ handler })(উপরে বর্ণিত হিসাবে) কল করা হচ্ছে। -
preventDefault()কল করা হচ্ছে, যা নেভিগেশন সম্পূর্ণরূপে বাতিল করতে পারে।
এই উদাহরণে ইভেন্টে intercept() কল করা হয়। ব্রাউজারটি আপনার handler কলব্যাক কল করে, যা আপনার সাইটের পরবর্তী অবস্থা কনফিগার করবে। এটি একটি ট্রানজিশন অবজেক্ট, navigation.transition তৈরি করবে, যা অন্যান্য কোড নেভিগেশনের অগ্রগতি ট্র্যাক করতে ব্যবহার করতে পারে।
intercept() এবং preventDefault() উভয়ই সাধারণত অনুমোদিত, কিন্তু এমন কিছু ক্ষেত্রে আছে যেখানে সেগুলিকে কল করা সম্ভব হয় না। যদি নেভিগেশনটি ক্রস-অরিজিন নেভিগেশন হয় তবে আপনি intercept() এর মাধ্যমে নেভিগেশন পরিচালনা করতে পারবেন না। এবং যদি ব্যবহারকারী তাদের ব্রাউজারে Back বা Forward বোতাম টিপে থাকেন তবে আপনি preventDefault() এর মাধ্যমে একটি নেভিগেশন বাতিল করতে পারবেন না; আপনি আপনার সাইটে আপনার ব্যবহারকারীদের ফাঁদে ফেলতে সক্ষম হবেন না। (এটি GitHub এ আলোচনা করা হচ্ছে ।)
এমনকি যদি আপনি নেভিগেশন বন্ধ করতে বা আটকাতে না পারেন, তবুও "navigate" ইভেন্টটি সক্রিয় থাকবে। এটি তথ্যবহুল , তাই আপনার কোড, উদাহরণস্বরূপ, একটি Analytics ইভেন্ট লগ করতে পারে যা নির্দেশ করে যে একজন ব্যবহারকারী আপনার সাইট ছেড়ে যাচ্ছেন।
প্ল্যাটফর্মে আরেকটি ইভেন্ট কেন যোগ করবেন?
একটি "navigate" ইভেন্ট লিসেনার একটি SPA-এর ভিতরে URL পরিবর্তনগুলি পরিচালনা করাকে কেন্দ্রীভূত করে। পুরানো API ব্যবহার করে এটি একটি কঠিন প্রস্তাব। আপনি যদি কখনও History API ব্যবহার করে আপনার নিজস্ব SPA-এর জন্য রাউটিং লিখে থাকেন, তাহলে আপনি হয়তো এইরকম কোড যোগ করেছেন:
function updatePage(event) {
event.preventDefault(); // we're handling this link
window.history.pushState(null, '', event.target.href);
// TODO: set up page based on new URL
}
const links = [...document.querySelectorAll('a[href]')];
links.forEach(link => link.addEventListener('click', updatePage));
এটা ঠিক আছে, কিন্তু সম্পূর্ণ নয়। লিঙ্কগুলি আপনার পৃষ্ঠায় আসতে পারে এবং যেতে পারে, এবং ব্যবহারকারীরা পৃষ্ঠাগুলির মাধ্যমে নেভিগেট করার জন্য এগুলিই একমাত্র উপায় নয়। উদাহরণস্বরূপ, তারা একটি ফর্ম জমা দিতে পারে অথবা এমনকি একটি চিত্র মানচিত্র ব্যবহার করতে পারে। আপনার পৃষ্ঠাটি এইগুলির সাথে মোকাবিলা করতে পারে, তবে সম্ভাবনার একটি দীর্ঘ লেজ রয়েছে যা কেবল সরলীকৃত করা যেতে পারে - এমন কিছু যা নতুন নেভিগেশন API অর্জন করে।
উপরন্তু, উপরেরটি পিছনে/ফরোয়ার্ড নেভিগেশন পরিচালনা করে না। এর জন্য আরেকটি ইভেন্ট আছে, "popstate" ।
ব্যক্তিগতভাবে, History API প্রায়শই মনে করে যে এটি এই সম্ভাবনাগুলিতে কিছুটা সাহায্য করতে পারে। তবে, এর আসলে কেবল দুটি পৃষ্ঠতল রয়েছে: ব্যবহারকারী তাদের ব্রাউজারে Back বা Forward চাপলে সাড়া দেওয়া, এবং URL গুলি পুশ করা এবং প্রতিস্থাপন করা। এটি "navigate" এর সাথে কোনও সাদৃশ্য রাখে না, উদাহরণস্বরূপ, উপরে দেখানো হিসাবে, যদি আপনি ক্লিক ইভেন্টের জন্য ম্যানুয়ালি শ্রোতাদের সেট আপ করেন।
নেভিগেশন কীভাবে পরিচালনা করবেন তা নির্ধারণ করা
navigateEvent এ নেভিগেশন সম্পর্কে অনেক তথ্য রয়েছে যা আপনি একটি নির্দিষ্ট নেভিগেশন কীভাবে মোকাবেলা করবেন তা নির্ধারণ করতে ব্যবহার করতে পারেন।
মূল বৈশিষ্ট্যগুলি হল:
-
canIntercept - যদি এটি মিথ্যা হয়, তাহলে আপনি নেভিগেশন আটকাতে পারবেন না। ক্রস-অরিজিন নেভিগেশন এবং ক্রস-ডকুমেন্ট ট্র্যাভার্সাল আটকানো যাবে না।
-
destination.url - নেভিগেশন পরিচালনা করার সময় সম্ভবত সবচেয়ে গুরুত্বপূর্ণ তথ্য যা বিবেচনা করা উচিত।
-
hashChange - যদি নেভিগেশনটি একই-ডকুমেন্ট হয় এবং হ্যাশটি URL-এর একমাত্র অংশ যা বর্তমান URL থেকে আলাদা হয়, তাহলে এটি সত্য হবে। আধুনিক SPA-তে, হ্যাশটি বর্তমান ডকুমেন্টের বিভিন্ন অংশের সাথে লিঙ্ক করার জন্য হওয়া উচিত। সুতরাং, যদি
hashChangeসত্য হয়, তাহলে সম্ভবত আপনার এই নেভিগেশনটি আটকানোর প্রয়োজন নেই। -
downloadRequest - যদি এটি সত্য হয়, তাহলে নেভিগেশনটি একটি
downloadঅ্যাট্রিবিউট সহ একটি লিঙ্ক দ্বারা শুরু হয়েছিল। বেশিরভাগ ক্ষেত্রে, আপনাকে এটি আটকানোর প্রয়োজন হয় না। -
formData - যদি এটি null না হয়, তাহলে এই নেভিগেশনটি POST ফর্ম জমা দেওয়ার অংশ। নেভিগেশন পরিচালনা করার সময় এটি বিবেচনা করুন। আপনি যদি শুধুমাত্র GET নেভিগেশন পরিচালনা করতে চান, তাহলে যেখানে
formDatanull নেই সেখানে নেভিগেশন আটকানো এড়িয়ে চলুন। ফর্ম জমা পরিচালনা করার উদাহরণটি নিবন্ধের পরে দেখুন। -
navigationType - এটি
"reload","push","replace", অথবা"traverse"এর মধ্যে একটি। যদি এটি"traverse"হয়, তাহলেpreventDefault()এর মাধ্যমে এই নেভিগেশন বাতিল করা যাবে না।
উদাহরণস্বরূপ, প্রথম উদাহরণে ব্যবহৃত shouldNotIntercept ফাংশনটি এরকম কিছু হতে পারে:
function shouldNotIntercept(navigationEvent) {
return (
!navigationEvent.canIntercept ||
// If this is just a hashChange,
// just let the browser handle scrolling to the content.
navigationEvent.hashChange ||
// If this is a download,
// let the browser perform the download.
navigationEvent.downloadRequest ||
// If this is a form submission,
// let that go to the server.
navigationEvent.formData
);
}
বাধাদান
যখন আপনার কোডটি তার "navigate" শ্রোতার ভেতর থেকে intercept({ handler }) কল করে, তখন এটি ব্রাউজারকে জানায় যে এটি এখন নতুন, আপডেট হওয়া অবস্থার জন্য পৃষ্ঠাটি প্রস্তুত করছে এবং নেভিগেশনে কিছুটা সময় লাগতে পারে।
ব্রাউজারটি বর্তমান অবস্থার জন্য স্ক্রোল পজিশন ক্যাপচার করে শুরু করে, যাতে এটি পরে ঐচ্ছিকভাবে পুনরুদ্ধার করা যায়, তারপর এটি আপনার handler কলব্যাক কল করে। যদি আপনার handler একটি প্রতিশ্রুতি ফেরত দেয় (যা async ফাংশনের সাথে স্বয়ংক্রিয়ভাবে ঘটে), সেই প্রতিশ্রুতি ব্রাউজারকে বলে যে নেভিগেশনটি কতক্ষণ সময় নেয় এবং এটি সফল কিনা।
navigation.addEventListener('navigate', navigateEvent => {
if (shouldNotIntercept(navigateEvent)) return;
const url = new URL(navigateEvent.destination.url);
if (url.pathname.startsWith('/articles/')) {
navigateEvent.intercept({
async handler() {
const articleContent = await getArticleContent(url.pathname);
renderArticlePage(articleContent);
},
});
}
});
এইভাবে, এই API একটি শব্দার্থিক ধারণা প্রবর্তন করে যা ব্রাউজার বুঝতে পারে: একটি SPA নেভিগেশন বর্তমানে ঘটছে, সময়ের সাথে সাথে, ডকুমেন্টটিকে পূর্ববর্তী URL এবং অবস্থা থেকে নতুন URL এ পরিবর্তন করে। এর বেশ কয়েকটি সম্ভাব্য সুবিধা রয়েছে, যার মধ্যে অ্যাক্সেসযোগ্যতা অন্তর্ভুক্ত: ব্রাউজারগুলি নেভিগেশনের শুরু, শেষ বা সম্ভাব্য ব্যর্থতা প্রকাশ করতে পারে। উদাহরণস্বরূপ, Chrome তার নেটিভ লোডিং সূচক সক্রিয় করে এবং ব্যবহারকারীকে স্টপ বোতামের সাথে ইন্টারঅ্যাক্ট করার অনুমতি দেয়। (ব্যবহারকারী যখন ব্যাক/ফরোয়ার্ড বোতামগুলির মাধ্যমে নেভিগেট করে তখন এটি বর্তমানে ঘটে না, তবে এটি শীঘ্রই ঠিক করা হবে ।)
নেভিগেশন কমিট করা হচ্ছে
নেভিগেশন আটকানোর সময়, আপনার handler কলব্যাক কল করার ঠিক আগে নতুন URL কার্যকর হবে। আপনি যদি অবিলম্বে DOM আপডেট না করেন, তাহলে এটি এমন একটি সময়কাল তৈরি করে যেখানে নতুন URL এর সাথে পুরানো সামগ্রী প্রদর্শিত হবে। এটি ডেটা আনার সময় বা নতুন সাবরিসোর্স লোড করার সময় আপেক্ষিক URL রেজোলিউশনের মতো বিষয়গুলিকে প্রভাবিত করে।
গিটহাবে URL পরিবর্তন বিলম্বিত করার একটি উপায় নিয়ে আলোচনা করা হচ্ছে, তবে সাধারণত আগত সামগ্রীর জন্য কোনও ধরণের স্থানধারক দিয়ে পৃষ্ঠাটি তাৎক্ষণিকভাবে আপডেট করার পরামর্শ দেওয়া হয়:
navigation.addEventListener('navigate', navigateEvent => {
if (shouldNotIntercept(navigateEvent)) return;
const url = new URL(navigateEvent.destination.url);
if (url.pathname.startsWith('/articles/')) {
navigateEvent.intercept({
async handler() {
// The URL has already changed, so quickly show a placeholder.
renderArticlePagePlaceholder();
// Then fetch the real data.
const articleContent = await getArticleContent(url.pathname);
renderArticlePage(articleContent);
},
});
}
});
এটি কেবল URL রেজোলিউশনের সমস্যাগুলিই এড়ায় না, এটি দ্রুতও বোধ করে কারণ আপনি তাৎক্ষণিকভাবে ব্যবহারকারীর সাথে সাড়া দিচ্ছেন।
সংকেত বাতিল করুন
যেহেতু আপনি একটি intercept() হ্যান্ডলারে অ্যাসিনক্রোনাস কাজ করতে সক্ষম, তাই নেভিগেশনটি অপ্রয়োজনীয় হয়ে যাওয়ার সম্ভাবনা রয়েছে। এটি তখন ঘটে যখন:
- ব্যবহারকারী অন্য একটি লিঙ্কে ক্লিক করেন, অথবা কোনও কোড অন্য একটি নেভিগেশন সম্পাদন করে। এই ক্ষেত্রে পুরানো নেভিগেশনটি নতুন নেভিগেশনের পক্ষে পরিত্যাগ করা হয়।
- ব্যবহারকারী ব্রাউজারে 'স্টপ' বোতামটি ক্লিক করেন।
এই সম্ভাবনাগুলির যেকোনো একটি মোকাবেলা করার জন্য, "navigate" শ্রোতার কাছে পাঠানো ইভেন্টটিতে একটি signal বৈশিষ্ট্য রয়েছে, যা একটি AbortSignal । আরও তথ্যের জন্য Abortable fetch দেখুন।
সংক্ষিপ্ত সংস্করণটি হল এটি মূলত এমন একটি অবজেক্ট প্রদান করে যা আপনার কাজ বন্ধ করার সময় একটি ইভেন্ট ফায়ার করে। উল্লেখযোগ্যভাবে, আপনি fetch() এ করা যেকোনো কলে একটি AbortSignal পাস করতে পারেন, যা নেভিগেশন প্রি-এমপ্ট করা থাকলে ইন-ফ্লাইট নেটওয়ার্ক অনুরোধ বাতিল করবে। এটি ব্যবহারকারীর ব্যান্ডউইথ সংরক্ষণ করবে এবং fetch() দ্বারা প্রদত্ত Promise প্রত্যাখ্যান করবে, যা DOM আপডেট করার মতো কোনও পদক্ষেপ থেকে নিম্নলিখিত কোডগুলিকে আটকাবে, যেমন একটি অবৈধ পৃষ্ঠা নেভিগেশন দেখানো।
এখানে পূর্ববর্তী উদাহরণটি দেওয়া হল, কিন্তু getArticleContent ইনলাইন করা হয়েছে, যেখানে দেখানো হয়েছে কিভাবে AbortSignal fetch() এর সাথে ব্যবহার করা যেতে পারে:
navigation.addEventListener('navigate', navigateEvent => {
if (shouldNotIntercept(navigateEvent)) return;
const url = new URL(navigateEvent.destination.url);
if (url.pathname.startsWith('/articles/')) {
navigateEvent.intercept({
async handler() {
// The URL has already changed, so quickly show a placeholder.
renderArticlePagePlaceholder();
// Then fetch the real data.
const articleContentURL = new URL(
'/get-article-content',
location.href
);
articleContentURL.searchParams.set('path', url.pathname);
const response = await fetch(articleContentURL, {
signal: navigateEvent.signal,
});
const articleContent = await response.json();
renderArticlePage(articleContent);
},
});
}
});
স্ক্রোল হ্যান্ডলিং
যখন আপনি একটি নেভিগেশন intercept() , তখন ব্রাউজার স্বয়ংক্রিয়ভাবে স্ক্রোলিং পরিচালনা করার চেষ্টা করবে।
একটি নতুন ইতিহাস এন্ট্রিতে নেভিগেশনের জন্য (যখন navigationEvent.navigationType "push" বা "replace" হয়), এর অর্থ হল URL খণ্ড ( # এর পরে বিট) দ্বারা নির্দেশিত অংশে স্ক্রোল করার চেষ্টা করা, অথবা স্ক্রোলটিকে পৃষ্ঠার শীর্ষে রিসেট করা।
রিলোড এবং ট্র্যাভার্সালের জন্য, এর অর্থ হল স্ক্রোলের অবস্থানটি আগের অবস্থানে পুনরুদ্ধার করা যেখানে এই ইতিহাস এন্ট্রিটি প্রদর্শিত হয়েছিল।
ডিফল্টরূপে, আপনার handler দ্বারা প্রদত্ত প্রতিশ্রুতি সমাধান হয়ে গেলে এটি ঘটে, তবে যদি আগে স্ক্রোল করা যুক্তিসঙ্গত হয়, তাহলে আপনি navigateEvent.scroll() কল করতে পারেন:
navigation.addEventListener('navigate', navigateEvent => {
if (shouldNotIntercept(navigateEvent)) return;
const url = new URL(navigateEvent.destination.url);
if (url.pathname.startsWith('/articles/')) {
navigateEvent.intercept({
async handler() {
const articleContent = await getArticleContent(url.pathname);
renderArticlePage(articleContent);
navigateEvent.scroll();
const secondaryContent = await getSecondaryContent(url.pathname);
addSecondaryContent(secondaryContent);
},
});
}
});
বিকল্পভাবে, আপনি intercept() এর scroll বিকল্পটি "manual" এ সেট করে সম্পূর্ণরূপে স্বয়ংক্রিয় স্ক্রোল হ্যান্ডলিং থেকে বেরিয়ে আসতে পারেন:
navigateEvent.intercept({
scroll: 'manual',
async handler() {
// …
},
});
ফোকাস হ্যান্ডলিং
আপনার handler প্রতিশ্রুতি পূরণ হয়ে গেলে, ব্রাউজারটি autofocus অ্যাট্রিবিউট সেট সহ প্রথম উপাদানটিতে ফোকাস করবে, অথবা যদি কোনও উপাদানে সেই বৈশিষ্ট্য না থাকে তবে <body> উপাদানটিতে ফোকাস করবে।
আপনি intercept() এর focusReset বিকল্পটি "manual" এ সেট করে এই আচরণ থেকে বেরিয়ে আসতে পারেন:
navigateEvent.intercept({
focusReset: 'manual',
async handler() {
// …
},
});
সাফল্য এবং ব্যর্থতার ঘটনা
যখন আপনার intercept() হ্যান্ডলারটি কল করা হবে, তখন দুটি জিনিসের মধ্যে একটি ঘটবে:
- যদি ফেরত দেওয়া
Promiseপূরণ হয় (অথবা আপনিintercept()কল না করেন), তাহলে Navigation API"navigatesuccess"একটিEventদিয়ে ফায়ার করবে। - যদি ফেরত দেওয়া
Promiseপ্রত্যাখ্যান করে, তাহলে API"navigateerror"একটিErrorEventদিয়ে ফায়ার করবে।
এই ইভেন্টগুলি আপনার কোডকে কেন্দ্রীভূতভাবে সাফল্য বা ব্যর্থতার সাথে মোকাবিলা করার সুযোগ দেয়। উদাহরণস্বরূপ, আপনি পূর্বে প্রদর্শিত অগ্রগতি সূচকটি লুকিয়ে সাফল্যের সাথে মোকাবিলা করতে পারেন, যেমন:
navigation.addEventListener('navigatesuccess', event => {
loadingIndicator.hidden = true;
});
অথবা আপনি ব্যর্থতার উপর একটি ত্রুটি বার্তা দেখাতে পারেন:
navigation.addEventListener('navigateerror', event => {
loadingIndicator.hidden = true; // also hide indicator
showMessage(`Failed to load page: ${event.message}`);
});
"navigateerror" ইভেন্ট লিসেনার, যা একটি ErrorEvent গ্রহণ করে, বিশেষভাবে কার্যকর কারণ এটি আপনার কোড থেকে যে কোনও ত্রুটি পাওয়ার নিশ্চয়তা দেয় যা একটি নতুন পৃষ্ঠা সেট আপ করছে। আপনি কেবল await fetch() করতে পারেন যদি আপনি জানেন যে নেটওয়ার্ক অনুপলব্ধ থাকে, তাহলে ত্রুটিটি অবশেষে "navigateerror" এ রাউট করা হবে।
নেভিগেশন এন্ট্রি
navigation.currentEntry বর্তমান এন্ট্রিতে অ্যাক্সেস প্রদান করে। এটি এমন একটি অবজেক্ট যা ব্যবহারকারী এখন কোথায় আছেন তা বর্ণনা করে। এই এন্ট্রিতে বর্তমান URL, সময়ের সাথে সাথে এই এন্ট্রি সনাক্ত করতে ব্যবহার করা যেতে পারে এমন মেটাডেটা এবং ডেভেলপার-প্রদত্ত অবস্থা অন্তর্ভুক্ত রয়েছে।
মেটাডেটাতে key থাকে, প্রতিটি এন্ট্রির একটি অনন্য স্ট্রিং প্রোপার্টি যা বর্তমান এন্ট্রি এবং এর স্লটকে প্রতিনিধিত্ব করে। বর্তমান এন্ট্রির URL বা অবস্থা পরিবর্তন হলেও এই কীটি একই থাকে। এটি এখনও একই স্লটে থাকে। বিপরীতভাবে, যদি কোনও ব্যবহারকারী Back টিপে একই পৃষ্ঠাটি পুনরায় খোলে, তাহলে এই নতুন এন্ট্রিটি একটি নতুন স্লট তৈরি করার সাথে সাথে key পরিবর্তন হবে।
একজন ডেভেলপারের কাছে, key কার্যকর কারণ নেভিগেশন এপিআই আপনাকে একটি ম্যাচিং কী ব্যবহার করে ব্যবহারকারীকে সরাসরি একটি এন্ট্রিতে নেভিগেট করতে দেয়। আপনি এটি ধরে রাখতে সক্ষম, এমনকি অন্যান্য এন্ট্রির অবস্থাতেও, যাতে সহজেই এক পৃষ্ঠা থেকে অন্য পৃষ্ঠায় যেতে পারেন।
// On JS startup, get the key of the first loaded page
// so the user can always go back there.
const {key} = navigation.currentEntry;
backToHomeButton.onclick = () => navigation.traverseTo(key);
// Navigate away, but the button will always work.
await navigation.navigate('/another_url').finished;
রাজ্য
নেভিগেশন এপিআই "state" এর ধারণাটি তুলে ধরে, যা ডেভেলপার-প্রদত্ত তথ্য যা বর্তমান ইতিহাস এন্ট্রিতে স্থায়ীভাবে সংরক্ষণ করা হয়, কিন্তু যা ব্যবহারকারীর কাছে সরাসরি দৃশ্যমান হয় না। এটি ইতিহাস এপিআইতে history.state এর সাথে অত্যন্ত মিল, কিন্তু এর থেকে উন্নত।
নেভিগেশন API-তে, আপনি বর্তমান এন্ট্রির (অথবা যেকোনো এন্ট্রির) .getState() পদ্ধতিটি কল করে এর অবস্থার একটি অনুলিপি ফেরত দিতে পারেন:
console.log(navigation.currentEntry.getState());
ডিফল্টরূপে, এটি undefined হবে।
সেটিং অবস্থা
যদিও স্টেট অবজেক্টগুলিকে মিউটেট করা যেতে পারে, তবুও ইতিহাস এন্ট্রির সাথে সেই পরিবর্তনগুলি আবার সংরক্ষণ করা হয় না, তাই:
const state = navigation.currentEntry.getState();
console.log(state.count); // 1
state.count++;
console.log(state.count); // 2
// But:
console.info(navigation.currentEntry.getState().count); // will still be 1
স্ক্রিপ্ট নেভিগেশনের সময় স্টেট সেট করার সঠিক উপায় হল:
navigation.navigate(url, {state: newState});
// Or:
navigation.reload({state: newState});
যেখানে newState যেকোনো ক্লোনেবল অবজেক্ট হতে পারে।
যদি আপনি বর্তমান এন্ট্রির অবস্থা আপডেট করতে চান, তাহলে বর্তমান এন্ট্রিটি প্রতিস্থাপন করে এমন একটি নেভিগেশন করা ভাল:
navigation.navigate(location.href, {state: newState, history: 'replace'});
তারপর, আপনার "navigate" ইভেন্ট লিসেনার navigateEvent.destination এর মাধ্যমে এই পরিবর্তনটি নিতে পারবেন:
navigation.addEventListener('navigate', navigateEvent => {
console.log(navigateEvent.destination.getState());
});
সিঙ্ক্রোনাসভাবে অবস্থা আপডেট করা হচ্ছে
সাধারণত, navigation.reload({state: newState}) এর মাধ্যমে অ্যাসিঙ্ক্রোনাসভাবে স্টেট আপডেট করা ভালো, তাহলে আপনার "navigate" লিসেনর সেই স্টেটটি প্রয়োগ করতে পারবেন। তবে, কখনও কখনও আপনার কোডটি শোনার সময় স্টেট পরিবর্তনটি ইতিমধ্যেই সম্পূর্ণরূপে প্রয়োগ হয়ে যায়, যেমন যখন ব্যবহারকারী একটি <details> উপাদান টগল করেন, অথবা ব্যবহারকারী একটি ফর্ম ইনপুটের স্টেট পরিবর্তন করেন। এই ক্ষেত্রে, আপনি স্টেট আপডেট করতে চাইতে পারেন যাতে এই পরিবর্তনগুলি পুনরায় লোড এবং ট্র্যাভার্সালের মাধ্যমে সংরক্ষণ করা যায়। updateCurrentEntry() ব্যবহার করে এটি সম্ভব:
navigation.updateCurrentEntry({state: newState});
এই পরিবর্তন সম্পর্কে শোনার জন্য একটি অনুষ্ঠানও রয়েছে:
navigation.addEventListener('currententrychange', () => {
console.log(navigation.currentEntry.getState());
});
কিন্তু, যদি আপনি "currententrychange" তে state পরিবর্তনের প্রতি প্রতিক্রিয়া দেখান, তাহলে আপনি আপনার state-handing কোডটিকে "navigate" ইভেন্ট এবং "currententrychange" ইভেন্টের মধ্যে বিভক্ত বা এমনকি ডুপ্লিকেট করতে পারেন, যেখানে navigation.reload({state: newState}) আপনাকে এটি এক জায়গায় পরিচালনা করতে দেবে।
অবস্থা বনাম URL প্যারামিটার
যেহেতু state একটি স্ট্রাকচার্ড অবজেক্ট হতে পারে, তাই আপনার সমস্ত অ্যাপ্লিকেশন স্টেটের জন্য এটি ব্যবহার করা লোভনীয়। তবে, অনেক ক্ষেত্রে URL-এ সেই স্টেটটি সংরক্ষণ করা ভালো।
যদি আপনি আশা করেন যে ব্যবহারকারী যখন অন্য ব্যবহারকারীর সাথে URL শেয়ার করবেন তখন অবস্থাটি ধরে রাখা হবে, তাহলে এটি URL-এ সংরক্ষণ করুন। অন্যথায়, state object হল ভালো বিকল্প।
সমস্ত এন্ট্রি অ্যাক্সেস করুন
"বর্তমান এন্ট্রি"ই সব নয়। API আপনার সাইট ব্যবহার করার সময় ব্যবহারকারী যে সমস্ত এন্ট্রি ব্যবহার করেছেন তার সম্পূর্ণ তালিকা navigation.entries() কলের মাধ্যমে অ্যাক্সেস করার একটি উপায়ও প্রদান করে, যা এন্ট্রিগুলির একটি স্ন্যাপশট অ্যারে প্রদান করে। এটি ব্যবহার করা যেতে পারে, উদাহরণস্বরূপ, ব্যবহারকারী কীভাবে একটি নির্দিষ্ট পৃষ্ঠায় নেভিগেট করেছেন তার উপর ভিত্তি করে একটি ভিন্ন UI দেখানোর জন্য, অথবা কেবল পূর্ববর্তী URL গুলি বা তাদের অবস্থাগুলি দেখার জন্য। বর্তমান ইতিহাস API এর সাথে এটি অসম্ভব।
আপনি পৃথক NavigationHistoryEntry গুলিতে একটি "dispose" ইভেন্ট শুনতে পারেন, যা ব্রাউজারের ইতিহাসের অংশ না থাকলে বন্ধ হয়ে যায়। এটি সাধারণ পরিষ্কারের অংশ হিসাবে ঘটতে পারে, তবে নেভিগেট করার সময়ও ঘটতে পারে। উদাহরণস্বরূপ, যদি আপনি 10 টি স্থান পিছনে যান, তারপর সামনের দিকে নেভিগেট করেন, তাহলে সেই 10 টি ইতিহাস এন্ট্রি নিষ্পত্তি করা হবে।
উদাহরণ
"navigate" ইভেন্টটি সকল ধরণের নেভিগেশনের জন্য কার্যকর, যেমনটি উপরে উল্লেখ করা হয়েছে। (সকল সম্ভাব্য ধরণের স্পেসিফিকেশনে আসলে একটি দীর্ঘ পরিশিষ্ট রয়েছে।)
যদিও অনেক সাইটের ক্ষেত্রে সবচেয়ে সাধারণ ঘটনা হল যখন ব্যবহারকারী একটি <a href="..."> ক্লিক করেন, দুটি উল্লেখযোগ্য, আরও জটিল নেভিগেশন প্রকার রয়েছে যা বিবেচনা করার যোগ্য।
প্রোগ্রাম্যাটিক নেভিগেশন
প্রথমটি হল প্রোগ্রাম্যাটিক নেভিগেশন, যেখানে আপনার ক্লায়েন্ট-সাইড কোডের ভিতরে একটি মেথড কলের মাধ্যমে নেভিগেশন করা হয়।
আপনি আপনার কোডের যেকোনো জায়গা থেকে navigation.navigate('/another_page') কল করে নেভিগেশন করতে পারেন। এটি "navigate" লিসেনারের সাথে নিবন্ধিত সেন্ট্রালাইজড ইভেন্ট লিসেনার দ্বারা পরিচালিত হবে এবং আপনার সেন্ট্রালাইজড লিসেনারকে সিঙ্ক্রোনাসলি কল করা হবে।
এটি location.assign() এবং friends এর মতো পুরানো পদ্ধতিগুলির উন্নত সমষ্টি, এবং History API এর পদ্ধতি pushState() এবং replaceState() উদ্দেশ্যে তৈরি।
navigation.navigate() পদ্ধতিটি এমন একটি বস্তু ফেরত দেয় যার মধ্যে { committed, finished } তে দুটি Promise ইনস্ট্যান্স থাকে। এটি ইনভোকারকে ট্রানজিশন "committed" (দৃশ্যমান URL পরিবর্তন করা হয়েছে এবং একটি নতুন NavigationHistoryEntry উপলব্ধ) অথবা "finished" ( intercept({ handler }) দ্বারা প্রদত্ত সমস্ত প্রতিশ্রুতি সম্পূর্ণ না হওয়া পর্যন্ত অপেক্ষা করতে দেয়—অথবা ব্যর্থতার কারণে বা অন্য নেভিগেশন দ্বারা প্রিএমপ্ট হওয়ার কারণে প্রত্যাখ্যাত)।
navigate পদ্ধতিতে একটি অপশন অবজেক্টও রয়েছে, যেখানে আপনি সেট করতে পারেন:
-
state: নতুন ইতিহাস এন্ট্রির অবস্থা, যাNavigationHistoryEntryতে.getState()পদ্ধতির মাধ্যমে উপলব্ধ। -
history: যা বর্তমান ইতিহাস এন্ট্রি প্রতিস্থাপনের জন্য"replace"এ সেট করা যেতে পারে। -
info:navigateEvent.infoএর মাধ্যমে নেভিগেট ইভেন্টে পাঠানোর জন্য একটি অবজেক্ট।
বিশেষ করে, info , উদাহরণস্বরূপ, একটি নির্দিষ্ট অ্যানিমেশন বোঝাতে কার্যকর হতে পারে যার ফলে পরবর্তী পৃষ্ঠাটি প্রদর্শিত হয়। (বিকল্প হতে পারে একটি গ্লোবাল ভেরিয়েবল সেট করা অথবা #hash-এর অংশ হিসেবে এটি অন্তর্ভুক্ত করা। উভয় বিকল্পই কিছুটা বিশ্রী।) উল্লেখযোগ্যভাবে, যদি কোনও ব্যবহারকারী পরবর্তীতে নেভিগেশনের কারণ হয়, যেমন তাদের ব্যাক এবং ফরোয়ার্ড বোতামগুলির মাধ্যমে, তাহলে এই info পুনরায় দেখানো হবে না। আসলে, এই ক্ষেত্রে এটি সর্বদা undefined থাকবে।
navigation আরও অনেক নেভিগেশন পদ্ধতি রয়েছে, যেগুলো { committed, finished } ধারণকারী একটি বস্তু ফেরত পাঠায়। আমি ইতিমধ্যেই traverseTo() (যা ব্যবহারকারীর ইতিহাসে একটি নির্দিষ্ট এন্ট্রি নির্দেশ করে এমন একটি key গ্রহণ করে) এবং navigate() উল্লেখ করেছি। এতে back() , forward() এবং reload() ও অন্তর্ভুক্ত রয়েছে। এই পদ্ধতিগুলি - ঠিক navigate() এর মতো - কেন্দ্রীভূত "navigate" ইভেন্ট লিসেনার দ্বারা পরিচালিত হয়।
ফর্ম জমা
দ্বিতীয়ত, POST এর মাধ্যমে HTML <form> জমা দেওয়া একটি বিশেষ ধরণের নেভিগেশন, এবং নেভিগেশন API এটিকে আটকাতে পারে। যদিও এতে একটি অতিরিক্ত পেলোড অন্তর্ভুক্ত থাকে, তবুও নেভিগেশনটি "navigate" শ্রোতা দ্বারা কেন্দ্রীয়ভাবে পরিচালিত হয়।
NavigateEvent এ formData প্রোপার্টি অনুসন্ধান করে ফর্ম জমা সনাক্ত করা যেতে পারে। এখানে একটি উদাহরণ দেওয়া হল যা যেকোনো ফর্ম জমাকে fetch() এর মাধ্যমে বর্তমান পৃষ্ঠায় থাকা ফর্মে রূপান্তরিত করে:
navigation.addEventListener('navigate', navigateEvent => {
if (navigateEvent.formData && navigateEvent.canIntercept) {
// User submitted a POST form to a same-domain URL
// (If canIntercept is false, the event is just informative:
// you can't intercept this request, although you could
// likely still call .preventDefault() to stop it completely).
navigateEvent.intercept({
// Since we don't update the DOM in this navigation,
// don't allow focus or scrolling to reset:
focusReset: 'manual',
scroll: 'manual',
handler() {
await fetch(navigateEvent.destination.url, {
method: 'POST',
body: navigateEvent.formData,
});
// You could navigate again with {history: 'replace'} to change the URL here,
// which might indicate "done"
},
});
}
});
কি বাদ পড়েছে?
"navigate" ইভেন্ট লিসেনারের কেন্দ্রীভূত প্রকৃতি থাকা সত্ত্বেও, বর্তমান নেভিগেশন API স্পেসিফিকেশন কোনও পৃষ্ঠার প্রথম লোডে "navigate" ট্রিগার করে না। এবং যে সাইটগুলি সমস্ত রাজ্যের জন্য সার্ভার সাইড রেন্ডারিং (SSR) ব্যবহার করে, তাদের জন্য এটি ঠিক থাকতে পারে - আপনার সার্ভার সঠিক প্রাথমিক অবস্থা ফিরিয়ে দিতে পারে, যা আপনার ব্যবহারকারীদের কাছে সামগ্রী পৌঁছানোর দ্রুততম উপায়। কিন্তু যে সাইটগুলি তাদের পৃষ্ঠা তৈরি করার জন্য ক্লায়েন্ট-সাইড কোড ব্যবহার করে তাদের পৃষ্ঠাটি শুরু করার জন্য একটি অতিরিক্ত ফাংশন তৈরি করতে হতে পারে।
নেভিগেশন API-এর আরেকটি ইচ্ছাকৃত নকশা পছন্দ হল এটি শুধুমাত্র একটি একক ফ্রেমের মধ্যে কাজ করে—অর্থাৎ, শীর্ষ-স্তরের পৃষ্ঠা, অথবা একটি নির্দিষ্ট <iframe> । এর বেশ কয়েকটি আকর্ষণীয় প্রভাব রয়েছে যা spec এ আরও নথিভুক্ত করা হয়েছে , তবে বাস্তবে, এটি ডেভেলপারদের বিভ্রান্তি কমাবে। পূর্ববর্তী History API-তে ফ্রেমের জন্য সমর্থনের মতো বেশ কয়েকটি বিভ্রান্তিকর এজ কেস রয়েছে এবং পুনর্কল্পিত নেভিগেশন API শুরু থেকেই এই এজ কেসগুলি পরিচালনা করে।
পরিশেষে, ব্যবহারকারী যে এন্ট্রিগুলির মাধ্যমে নেভিগেট করেছেন তার তালিকা প্রোগ্রাম্যাটিকভাবে পরিবর্তন বা পুনর্বিন্যাস করার বিষয়ে এখনও ঐক্যমত্য হয়নি। এটি বর্তমানে আলোচনার অধীনে রয়েছে, তবে একটি বিকল্প হতে পারে শুধুমাত্র মুছে ফেলার অনুমতি দেওয়া: হয় ঐতিহাসিক এন্ট্রি অথবা "সমস্ত ভবিষ্যতের এন্ট্রি"। পরবর্তীটি অস্থায়ী অবস্থা অনুমোদন করবে। যেমন, একজন বিকাশকারী হিসাবে, আমি করতে পারি:
- নতুন URL বা স্টেটে নেভিগেট করে ব্যবহারকারীকে একটি প্রশ্ন জিজ্ঞাসা করুন
- ব্যবহারকারীকে তাদের কাজ সম্পূর্ণ করার অনুমতি দিন (অথবা ফিরে যান)
- কোনও কাজ শেষ হওয়ার পরে ইতিহাসের এন্ট্রিটি সরিয়ে ফেলুন
এটি অস্থায়ী মোডাল বা ইন্টারস্টিশিয়ালের জন্য উপযুক্ত হতে পারে: নতুন URL হল এমন একটি জিনিস যা থেকে ব্যবহারকারীরা Back জেসচার ব্যবহার করে বেরিয়ে যেতে পারেন, কিন্তু তারপর তারা ভুলবশত Forward গিয়ে আবার এটি খুলতে পারবেন না (কারণ এন্ট্রিটি সরানো হয়েছে)। বর্তমান History API এর সাথে এটি সম্ভব নয়।
নেভিগেশন এপিআই ব্যবহার করে দেখুন
নেভিগেশন API Chrome 102 এ ফ্ল্যাগ ছাড়াই পাওয়া যাচ্ছে। আপনি Domenic Denicola এর একটি ডেমোও চেষ্টা করে দেখতে পারেন।
ক্লাসিক হিস্ট্রি এপিআই দেখতে সহজবোধ্য হলেও, এটি খুব একটা সুনির্দিষ্ট নয় এবং এর কর্নার কেস এবং ব্রাউজারগুলিতে এটি কীভাবে ভিন্নভাবে প্রয়োগ করা হয়েছে সে সম্পর্কে প্রচুর সমস্যা রয়েছে। আমরা আশা করি আপনি নতুন নেভিগেশন এপিআই সম্পর্কে প্রতিক্রিয়া জানাবেন।
তথ্যসূত্র
- WICG/নেভিগেশন-এপিআই
- মজিলা স্ট্যান্ডার্ডস পজিশন
- প্রোটোটাইপের উদ্দেশ্য
- ট্যাগ পর্যালোচনা
- Chrome স্থিতি এন্ট্রি
স্বীকৃতি
এই পোস্টটি পর্যালোচনা করার জন্য থমাস স্টেইনার , ডোমেনিক ডেনিকোলা এবং নেট চ্যাপিনকে ধন্যবাদ।