জাভাস্ক্রিপ্ট ডম ম্যানিপুলেশনঃ ইভেন্ট(Event)

ইভেন্ট হচ্ছে সোজা কথায় কোনো ঘটনা। আমি আমার একটা HTML ডকুমেন্ট এর কোথাও ক্লিক করলাম, এটা একটা ইভেন্ট। অথবা আমি একটা ইলিমেন্ট এর উপর মাউস পয়েন্টার নিলাম, অথবা আমি কী-বোর্ডের কোনো কী-তে চাপলাম, এগুলো সবই ইভেন্টের অন্তর্ভুক্ত। এমনিতে একটা পেজ লোড করা, পেজকে রিসাইজ করা এগুলোও ইভেন্টের মধ্যে পড়ে। এরকম আরো অনেক রকমের ইভেন্ট আছে যেগুলো আমাদের HTML ডকুমেন্ট এ, ডকুমেন্ট এর বিভিন্ন ইলিমেন্টে বিভিন্নভাবে ঘটতে পারে।

এখন এরকম কোনো ইভেন্ট এ আমরা হয়তো কোনো অ্যাকশন নিতে চাইতে পারি। যেমন আমরা হয়তো চাইতে পারি কেউ অমুক বাটনে ক্লিক করলে একটা ম্যাসেজ শো করাবো। অথবা আমাদের পেজটা পুরোপুরি লোড না হওয়া পর্যন্ত আমরা একটা লোডার শো করাবো। অথবা আমরা ফর্ম সাবমিট করার সময় ইনপুট ফিল্ডগুলো ভ্যালিড কিনা দেখবো। এগুলো সবই ইভেন্ট এর সাহায্যে করা হয়।

জাভাস্ক্রিপ্ট এর ইভেন্ট এর সাহায্যে একটা পেজ অনেকটা ডায়নামিক পেজের মতো করা ফেলা যায়। আমরা পেজ রিলোড না করেও অনেকসময় ইনস্ট্যান্ট ফলাফল, অ্যাকশন দেখতে পারি জাভাস্ক্রিপ্ট এর ইভেন্ট ব্যবহার করার মাধ্যমে। এর ফলে আমাদের একটা ওয়েবসাইটের ওভারল ইউজার এক্সপেরিয়েন্স ভালো লেভেলে নেওয়া যায়।

আমরা এই লেখায় প্র্যাক্টিসের সুবিধার্থে একটা প্রোজেক্টটা ব্যবহার করবো। তো এটা এখান থেকে ডাউনলোড করে আনজিপ করে আপনার পছন্দের কোড এডিটরে ওপেন করুন। আমরা আমাদের স্ক্রিপ্ট/কোডগুলো script.js ফাইলের ভিতরে লিখবো যেটা আমাদের index.html এর সাথে লিঙ্ক করা আছে।আর index.html টা ক্রোমে ওপেন করে ডেভেলপার কন্সোল ওপেন করে প্র্যাক্টিস করা শুরু করুন। প্রত্যেকবার স্ক্রিপ্ট ফাইলে নতুন কোনো কোড অ্যাড করলে সেটা ইফেক্ট দেখতে আমাদের ব্রাউজারে খুলে রাখা ডকুমেন্টটা অবশ্যই রিলোড করতে হবে। আমাদের প্র্যাক্টিস ডকুমেন্টটাতে কয়েকটা বক্স আছে। সেই সাথে কয়েকটা বাটন, একটা ফর্ম, কয়েকটা ইনপুট ফিল্ডসহ আরো কিছু ইলিমেন্ট আছে যেগুলো আমরা বিভিন্ন পর্যায়ে ব্যবহার করবোঃ

অ্যালার্ট বক্সঃ শুরু করার আগে আমরা একটা স্পেশাল ফাংশন দেখে নেই। এই ফাংশনের কাজ হচ্ছে আমাদের ব্রাউজারে অ্যালার্ট দেখানো। যেমন কন্সোলে সরাসরি যদি এরকম কোড লিখিঃ

alert('Hello World!');

এটা রান করার সাথে সাথেই আপনার ব্রাউজারে এরকম অ্যালার্ট বক্স দেখবেনঃ

১। কমন ডম ইভেন্টঃ আমাদের ডমে কমন কিছু ইভেন্ট আছে। আমরা সেই ইভেন্ট দুইরকমভাবে হ্যান্ডেল করতে পারি। প্রথমত ইনলাইন স্টাইলে, আমাদের কাঙ্খিত ইলিমেন্টের সাথেই অ্যাট্রিবিউটস হিসেবে। আর দ্বিতীয়ত আমাদের HTML ডমের সাহায্যে পুরোপুরি জাভাস্ক্রিপ্ট দিয়ে, ইলিমেন্টকে টাচ না করেই।

HTML ইভেন্ট অ্যাট্রিবিউটঃ আমরা চাইলে আমাদের ইলিমেন্ট এ অ্যাট্রিবিউট হিসেবেও সরাসরি ইভেন্ট হ্যান্ডেল করতে পারি। যেমন আমাদের ডকুমেন্টটায় বক্স ৪ এর প্রথম লিস্ট আইটেমে এরকম সরাসরি অ্যাট্রিবিউটের মাধ্যমে ক্লিক ইভেন্ট লাগাতে চাইঃ

<li onclick="alert('This Document is Created for https://js.zonayed.me')">About this document</li>

এটা লিখে ফাইল সেইভ করে, ব্রাউজারে আমাদের ডকুমেন্টটা রিলোড দিয়ে এই ইলিমেন্ট এর উপরে ক্লিক করলে একটা অ্যালার্ট বক্সে কিছু দেখতে পাবেনঃ

এখন আমরা এখানেই সব লিখে দিয়েছি, কিন্তু চাইলে একটা ফাংশন আলাদা করে তৈরী করে সেটা ক্লিকে কল হবে এরকমটাও বলে দিতে পারি। যেমন আমাদের ডকুমেন্টটার script.js ফাইলটা ওপেন করে এরকম একটা ফাংশন লিখুনঃ

function showAlert() {
alert('Alert by Function Call');
}

এখন আবার আমাদের সেই লিস্ট আইটেমে গিয়ে onclick এর ভিতরের সব মুছে শুধুমাত্র এই ফাংশনটা কল করুনঃ

<li onclick="showAlert()">About this document</li>

এখন ক্রোমে ওপেন করা ডকুমেন্টটা রিলোড দিয়ে যদি আবার সেই ইলিমেন্ট উপর ক্লিক করেন তাহলে দেখবেন এরকম কিছু আসছেঃ

তারমানে আমরা সফলভাবে অন্য একটা ফাইলে ফাংশন তৈরী করে সেটা অ্যাট্রিবিউটের মাধ্যমে কল করতে পেরেছি। এরকম বড় কোনো ফাংশানালিটি থাকলে এক লাইনে তো আর হয় না, এভাবেই আলাদা ফাইল নিয়ে সেখানে সেটা হ্যান্ডেল করতে হয়। এরকম অ্যাট্রিবিউটের মাধ্যমে আরো কিছু টাইপের ইভেন্ট নিয়ে কাজ করতে পারবেনঃ

  • onchange: আপনার ইলিমেন্ট এ কোনো ধরনের পরিবর্তন আসলে এই ইভেন্ট ঘটবে। সাধারণত আমরা যখন ফর্মের ইনপুট ফিল্ডে ডাটা দেই, তখন সেই ইনপুট ফিল্ডের ভ্যালু চেঞ্জ হয়। আর সেই চেঞ্জের সময়ের ইভেন্টটা এটা দিয়ে হ্যান্ডেল করতে পারবেন।
  • onclick: ইলিমেন্ট এর উপর ক্লিক করলে এই ইভেন্ট ঘটবে।
  • onmouseover: খালি পয়েন্টারটা আপনার কাঙ্খিত ইলিমেন্ট এর উপরে নিলেই এই ইভেন্ট ঘটবে।
  • onmouseout: কোনো ইলিমেন্ট থেকে আপনার মাউসের পয়েন্টার সরালে এই ইভেন্ট ঘটবে।
  • onkeydown: কী-বোর্ডের কোনো কী চাপলে এই ইভেন্ট ঘটবে।
  • onload: ব্রাউজার আপনার ইলিমেন্ট লোড শেষ করলে এই ইভেন্ট ঘটে।

এখানে আমি প্রত্যেকটা নিয়ে পৃথক পৃথকভাবে আলোচনা করলাম না, কারণ পরে এদেরকেই আবার বিভিন্ন জায়গায় দেখাবো। তাছাড়া এভাবে ইনলাইন, অ্যাট্রিবিউটের মাধ্যমে ইভেন্ট হ্যান্ডেল করাটাও খুব ভালো প্র্যাক্টিস না। আমরা সম্পূর্ণ জাভাস্ক্রিপ্ট দিয়েই সব করবো, আমাদের HTML ইলিমেন্ট টাচ না করেই।

— HTML ডমের সাহায্যেঃ উপরে আমরা HTML এ অ্যাট্রিবিউট হিসেবে যে ইভেন্টগুলো দেখলাম, সেগুলোসহ আরো বেশ কিছু ইভেন্ট আছে আমাদের যেগুলো পুরোপুরি জাভাস্ক্রিপ্ট দিয়েও হ্যান্ডেল করতে পারবো HTML ইলিমেন্ট টাচ না করেই।

আমরা আমাদের বক্স ১ এ একটা বাটন আছেঃ

বাটনে একটা ইভেন্ট লাগাবো। প্রথমে বাটনটা সিলেক্ট করে নিইঃ

const clickMeBtn = document.getElementById('btn-click-me');

এখন এভাবে প্রথমে ইলিমেন্ট সিলেক্ট করে ভ্যারিয়েবলে নিয়ে তারপর কাজ করা ভালো প্র্যাক্টিস। এখন এখানে clickMeBtn টাই হচ্ছে আমাদের বাটন। ব্রাউজারের কন্সোল থেকে এটা সরাসরি খুলে দেখলেও ভিতরে আপনি অনেকগুলো মেথড, প্রপার্টিসহ ইভেন্টগুলোও দেখতে পাবেনঃ

console.dir(clickMeBtn);

এখানে আরো অনেকরকমের ইভেন্ট আছে। কিন্তু আমরা তারমধ্যে ক্লিকের ইভেন্ট দেখবো। এখানে এই ইভেন্টগুলোতে আমরা ফাংশন অ্যাসাইন করে দিলে প্রত্যেকবার এই ইভেন্ট ঘটলেই সেই ফাংশন রান করবেঃ

clickMeBtn.onclick = function() {
console.log('Button Clicked');
}

আমরা এখানে ক্লিকে একটা ফাংশন অ্যাসাইন করেছি যেটার কাজ হচ্ছে একটা লেখা প্রিন্ট করা কন্সোলে। এখন প্রত্যেকবার আমাদের সেই বাটনে ক্লিক করলেই দেখবেন কন্সোলে উক্ত লেখা প্রিন্ট হচ্ছেঃ

এরকম সেইম টাইপের আরো অনেক ইভেন্ট আছে। যেমনঃ onmouseover, onchange, onblur , onload , onresize সহ আরো অনেক। সবগুলো একইভাবে কাজ করবে। যেমন আমরা চাইলে আরো একটা দেখতে পারি। আমরা যদি চাই আমাদের ইলিমেন্ট এর উপ মাউস নিলেই কিছু একটা অ্যাকশন নিবোঃ

clickMeBtn.onmouseover = function() {
console.log('Mouse Over');
}

এখন সেই বাটনের উপরে মাউস বা পয়েন্টারটা নিলেই দেখবেন আমাদের ফাংশন রান করছেঃ

২। ইভেন্টের লিসেনারঃ উপরে আমরা যেভাবে ইভেন্ট হ্যান্ডেলিং দেখলাম সেটা খুব বেশী ব্যবহার করা হয় না। বরং আমাদের আরেকটা মেথড আছে, একটা মেথড দিয়েই আমরা বিভিন্নরকমের ইভেন্ট হ্যান্ডেল করতে পারবো। তাছাড়া এই মেথডের বিশেষ কিছু সুবিধাও আছে যেমন আমরা এটার মাধ্যমে আনলিমিটেড ইভেন্ট অ্যাড করতে পারবো একটা ইলিমেন্ট এ। যেখানে একটু আগে উল্লেখ করা পদ্ধতিতে আমরা এক সাথে দুইটা ক্লিক ইভেন্ট অ্যাড করতে পারবো না, পরেরটা আগের ইভেন্টকে রিপ্লেস করে ফেলবে। এছাড়াও বাকী পার্থ্যক্যগুলো পরে আস্তে আস্তে আলোচনা করবো। সেই মেথডটা হচ্ছে addEventListener() । এটাও আমরা আমাদের ইলিমেন্ট এর সাথে ব্যবহার করতে পারবো।

এই addEventListener প্রধানত দুইটা প্যারামিটার নেয়। প্রথমটা হচ্ছে কোন টাইপের ইভেন্ট হবে সেটা, আর দ্বিতীয়টা একটা ফাংশন(হ্যান্ডেলার) যেটা ইভেন্ট ঘটলে রান হবেঃ

yourElement.addEventListener(typeofEvent, handler);

এখন আমরা আমাদের বক্স ১ সিলেক্ট করবোঃ

const box1 = document.getElementById('box1');

এখন আমরা এই বক্স ১ এ আমাদের ইভেন্ট লিসেনারটা লাগাবো। আর একই সাথে আমরা ক্লিক টাইপের ইভেন্ট চাচ্ছিঃ

box1.addEventListener('click', function() {
console.log('Clicked on Box 1');
})

ব্যাস, এখন বক্স ১ এর উপরে গিয়ে ক্লিক করলেই দেখবেন আপনার ফাংশন রান হচ্ছেঃ

এরকম আপনি চাইলে এখানে ইভেন্ট টাইপে প্যারামিটার হিসেবে load, focus, blur, submit, resize, cut, copy, keydown, keypress, keyup সহ আরো অনেক টাইপের ইভেন্ট দিতে পারবেন।

৩। ইভেন্ট মডিফাইঃ আমাদের বক্স ১ এ এখন পুরো বক্সের উপর একটা ক্লিক ইভেন্ট লাগানো আছে, আবার সেই সাথে বক্সের ভিতরের বাটনেও দুইটা ইভেন্ট লাগানো আছে। এখন আপনি যদি জাস্ট সেই বাটনে ক্লিক করেন, তাহলে দেখবেন সবগুল ইভেন্টই একসাথে কাজ করছেঃ

এটা বাই ডিফল্ট আচরণ, কিন্তু আমরা চাইলে এটা মডিফাই করতে পারবো।

বাবল আপ(Bubble Up): বাটনে ক্লিক করাতে আমাদের বক্সের ইভেন্টও রান করছে, এটাই মূলত বাবল আপ। আমাদের ক্লিক ইভেন্টটা বাটন থেকে বাবল আপ করে এর প্যারেন্ট ইলিমেন্ট বক্স ১ এও চলে গেছে। আর তাই সেই ইভেন্টও রান করেছে। এখন আমরা চাইলে এই আচরণটা অফ করে দিতে পারি, যদিও এই বাবল আপেরও বিশেষ সুবিধা আছে যেটা আমরা একটু পরেই আলোচনা করবো। সেজন্যে আমাদের বাটনের ইভেন্টটা মডিফাই করতে হবে এরকমভাবেঃ

clickMeBtn.onclick = function(event) {
event.stopPropagation();
console.log('Button Clicked');
}

এখানে আমাদের হ্যান্ডেলার ফাংশনটা event নামে একটা প্যারামিটার নিয়েছে, আপনি চাইলে যেকোনো নামেই নিতে পারবেন এই প্যারামিটার। এটার ভিতরে আপনার ইভেন্ট সম্পর্কে আরো অনেক মেথড, প্রপার্টি পাবেন। ঐখান থেকেই আমরা একটা মেথড stopPropagation() ব্যবহার করেছি এখানে। এটার কাজই হচ্ছে এরকম বাবল-আপ বন্ধ করা। ব্যাস, এখন ডকুমেন্টটা রিলোড দিয়ে বাটনে ক্লিক করলে দেখবেন শুধুমাত্র বাটনের ক্লিক ইভেন্ট হ্যান্ডেলারটাই চলছেঃ

সেইম ইলিমেন্ট এ একই রকম আরেকটা ইভেন্টঃ ধরি আমাদের বক্স ১ এর বাটনে আরেকটা ক্লিক ইভেন্ট আছেঃ

clickMeBtn.addEventListener('click', function() {
console.log('Another Click Event');
});

এটা লিখে সেইভ করে ব্রাউজার রিলোড দিয়ে আমাদের বাটনে ক্লিক করলে দেখবেন ক্লিক ইভেন্ট দুইটাই কাজ করছেঃ

এখন যদি আমরা চাই যে না আমরা শুধুমাত্র একটা ইভেন্ট রান করবে। তাহলে যে ইভেন্টটা রাখবেন সেখানে হ্যান্ডেলারে event প্যারামিটার পাস করে দিয়ে এটারই স্পেশাল আরেকটা মেথড আছে, সেটা কল করে দিতে হবেঃ

clickMeBtn.onclick = function(event) {
event.stopPropagation();
event.stopImmediatePropagation();
console.log('Button Clicked');
}

তবে মনে রাখবেন যে ইভেন্ট থেকে এটা বন্ধ করতে চাচ্ছেন সেটা বাকী ইভেন্টগুলো থেকে আপনার স্ক্রিপ্ট ফাইলে আগে থাকতে হবে। নতুবা সে ইভেন্টগুলো রান করার পর আপনার ব্রাউজার দেখতে পাবে যে আপনি ইভেন্ট রান করাতে না করেছেন। কিন্তু ততক্ষনে কোড উপরে থাকার কারণে আগেই রান হয়ে গেছে। তাই এটা দিয়েও কোনো লাভ হবে না।

ব্যাস, এখন সেইভ করে রিলোড দিয়ে আবার বাটনে ক্লিক করলে দেখবেন আগের ক্লিক ইভেন্টটা আর কাজ করছে নাঃ

ডিফল্ট আচরনঃ আমাদের বক্স ৪ এ কিছু লিঙ্ক আছে। এগুলোতে ক্লিক করলে আপনাকে কাঙ্ক্ষিত ওয়েবসাইটে নিয়ে যাবে। এরা লিঙ্ক, তাই ক্লিক করলে আপনাকে লিঙ্কের রেফারেন্স অনুযায়ী সে জায়গায় নিয়ে যাবে।

এটাই ব্রাউজারের ডিফল্ট আচরণ। এখন আমরা চাইলে এই আচরণটাকেও মডিফাই করে দিতে পারবো। আমাদের এখানে প্রথম লিঙ্কটা JavaScript DOM Manipulation নামে ইলিমেন্টটার লিস্ট ট্যাগের একটা আইডি আছেঃ

<li id="link"><a href="https://with.zonayed.me">JavaScript DOM Manipulation</a></li>

আমরা এই আইডি দিয়ে এটা সিলেক্ট করবো। তারপর ক্লিক ইভেন্টটাকে মডিফাই করবো। ক্লিক করলে যাতে বাই ডিফল্ট আচরণ না করে ব্রাউজার সেটা বলে দিবো। প্রথমে লিঙ্ক ট্যাগটা সিলেক্ট করে নেইঃ

const link = document.getElementById('link');

ব্যাস এখন সেই আগের মতো করে event প্যারামিটারেরই এরকম ডিফল্ট আচরণ অফ করার জন্যে একটা স্পেশাল মেথড আছে, সেটা আমরা ব্যবহার করতে পারবোঃ

link.addEventListener('click', function(event) {
event.preventDefault();
});

ব্যাস, এখন ডকুমেন্টটা সেইভ করে ব্রাউজারে রিলোড দিয়ে সেই কাঙ্ক্ষিত লিঙ্কে ক্লিক করে দেখবেন সেটা আর বাই ডিফল্ট আচরণ করছে না। এখানে একটা জিনিস খেয়াল করবেন যে আমরা ঠিক a ট্যাগটাকে সিলেক্ট করি নাই, বরং আমরা এর প্যারেন্ট li কে সিলেক্ট করেছি। আমরা এটা নিয়ে একটু পরেই বিস্তারিত আলোচনা করবো।

৪। নেটিভ(Native) ডম ইভেন্টঃ আমরা তো এতক্ষন নিজেরা নিজেরা ইভেন্ট ঘটালাম। নিজেরাই গিয়ে বাটনে ক্লিক করেছি, ক্লিক ইভেন্ট ঘটেছে। কিন্তু আমরা চাইলে কোড দিয়েও এরকম ইভেন্ট ঘটাতে পারি। ধরি আমরা আমাদের বক্স ১ এর বাটনে একটা ক্লিক ইভেন্ট ঘটাতে চাচ্ছি। আর তাই ব্রাউজারের কন্সোল ওপেন করে নিচের এই কোডটা লিখবো[এটা স্ক্রিপ্ট ফাইলের ভিতরে লেখার দরকার নাই]:

clickMeBtn.click();

এই কোডটা দেওয়ার সাথে সাথে দেখবেন বাটনের উপর একটা ক্লিক ইভেন্ট ঘটেছেঃ

আমাদের বাটনে ক্লিক ইভেন্ট হ্যান্ডেলার যেহেতু লাগানো আছে, তাই ঐটাই রান হয়েছে।

এরকম আরো ইভেন্ট যেমন focus , blur অথবা ফর্মের submit ইভেন্টও এভাবে ঘটানো যাবে।

৫। ইভেন্ট ডেলিগেশন(Delegation): একটু আগে আমরা ইভেন্ট বাবল-আপ নিয়ে কথা বলেছি। আমরা বাবল-আপ কিভাবে বন্ধ করবো সেটা দেখেছি। আর তাই মানে বাবল-আপ যে খারাপ জিনিস তা কিন্তু নয়। বাবল-আপের সুবিধাটাও আমাদের অনেক কাজে লাগতে পারে। যেমন আমরা একটা প্যারেন্ট ইলিমেন্ট থেকেও একটা চাইল্ড ইলিমেন্ট এর উপর ঘটে যাওয়া ইভেন্ট ধরে সেটাকে হ্যান্ডেল করতে পারি। এখানে সুবিধাটা কি? হ্যা, সুবিধা হচ্ছে একটা প্যারেন্ট ইলিমেন্ট এর অনেকগুলো চাইল্ড ইলিমেন্ট থাকতে পারে। এখন সবার জন্যে আলাদা আলাদা করে ইভেন্ট হ্যান্ডেল করা আসলে একটু ঝামেলার কাজই। সেক্ষেত্রে আমরা তাদের প্যারেন্ট এ ইভেন্টটা হ্যান্ডেল করতে পারি, যেহেতু দিন শেষে সেই চাইল্ড ইলিমেন্ট এর উপরে ঘটে যাওয়া ইভেন্ট বাবল-আপ করে প্যারেন্ট ইলিমেন্টেই আসবে।

তাছাড়া এমনও হতে পারে, আমাদের একটা ইলিমেন্ট ডমে নাই। আমরা কোনোভাবে পরে অ্যাড করবো। এখন ঐরকম একটা ইলিমেন্ট এর উপর কিভাবে ইভেন্ট নিয়ে কাজ করবো? হ্যা, সেখানেও আমরা প্যারেন্ট ইলিমেন্ট এর উপর ইভেন্ট হ্যান্ডেলিং এর কাজটা করতে পারবো। আর তাই কোনো ইলিমেন্ট না থাকলেও, পরে তৈরী করা হলেও সেটার উপর ঘটে যাওয়া ইভেন্ট যেই বাবল-আপ করে প্যারেন্টে আসবে, আমাদের হ্যান্ডেলার কাজ করতে পারবে।

যেমন আমাদের বক্স ৪ তে তিনটা লিস্ট আইটেম আছে। এখন সবার জন্যে পৃথক পৃথক করে ইভেন্ট না লাগিয়ে একেবারে লিস্ট তিনটার প্যারেন্ট বা একদম বক্স থেকেই এদের উপরের ঘটে যাওয়া ইভেন্ট অনুযায়ী অ্যাকশন নিতে পারবো। আর তাই প্রথমেই বক্স ৪ সিলেক্ট করে নিইঃ

var box4 = document.getElementById('box4');

এখন লিস্ট আইটেমগুলোয় ক্লিক করলে সেই ক্লিক ইভেন্ট বাবল-আপ করে প্যারেন্ট বক্স ৪ তে আসবে এখানে আমরা টার্গেট যাচাই করে অ্যাকশন নিবোঃ

box4.addEventListener('click', function(event) {
if(event.target.tagName === 'LI') {
console.log('Clicked on a List Item');
}
});

একটা জিনিস এখানে খেয়াল করবেন, আমরা লিস্ট আইটেমগুলো ডিটেক্ট করেছি tagName দিয়ে, আপনি চাইলে আপনার মতো আরো অন্যকিছু(ক্লাস, অ্যাট্রিবিউট) দিয়েও ডিটেক্ট করতে পারবেন। আর ট্যাগ নেইমটাও বড় হাতের হবে এখানে। এখন যেকোনো একটা লিস্ট আইটেমে ক্লিক করলেই দেখবেন হ্যান্ডেলার রান করছেঃ

এভাবে বাবল-আপ হওয়া ইভেন্টকে ক্যাপচার করে হ্যান্ডেল করার প্যাটার্নটাকেই মূলত ইভেন্ট ডেলিগেশন বলা হয়।

৬। ফর্ম ইভেন্টঃ কিছু ইভেন্ট আছে যেগুলো ফর্মে বেশ কাজে লাগে। আর তাই আলাদা করে সেগুলো এখানে আলোচনা করবো। ফর্মের ইনপুট ফিল্ডে ডাটা চেঞ্জ থেকে শুরু করে ফর্ম সাবমিট করার ইভেন্ট আমরা বিভিন্নভাবে হ্যান্ডেল করতে পারি। আমাদের ডকুমেন্ট এ একটা ফর্ম আছেঃ

আমরা এটাতেই বিভিন্নভাবে ইভেন্টগুলো কিভাবে কাজ করে দেখবো। সেজন্যে আমরা প্রথমে ইনপুট ফিল্ডগুলো সিলেক্ট করে নিবোঃ

const inputs = document.querySelectorAll('input');

এখানে inputs হচ্ছে নোডলিস্ট, আর এখানে দুইটা ইনপুট ফিল্ড থাকায় নোডলিস্টে ইন্ডেক্স ০ আর ১ এ ক্রমানুসারে আমাদের ইনপুট ইলিমেন্টগুলো সিলেক্ট হয়েছে। আমরা এখন ইনডেক্স নাম্বারের সাহায্যে ইলিমেন্টগুলো একটা একটা করে সিলেক্ট করতে পারবো।

আমরা আমাদের সাবমিট বাটনটাও সিলেক্ট করে রাখবো এভাবেঃ

const submitBtn = document.querySelector('button[type=submit]');

ইনপুট ফিল্ড চেঞ্জ ইভেন্টঃ ইনপুট ফিল্ডে কোনোরকম চেঞ্জ হলে সেখানে আমরা চেঞ্জ ইভেন্টের উপর ভিত্তি করে হ্যান্ডেলার লাগাতে পারবো। ধরি আমরা আমাদের প্রথম ইনপুট ফিল্ডে কোনোরকম চেঞ্জ হলে কিছু একটা করতে চাইঃ

inputs[0].addEventListener('change', function() {
console.log('Change Event on Input');
});

এখন আমাদের প্রথম ইনপুট ফিল্ডে কিছু লিখে অন্যকোথাও ক্লিক করলেই এই চেঞ্জ ইভেন্ট ঘটবে, আর সেই সাথে আমাদের কাঙ্ক্ষিত ফলাফলও কন্সোলে দেখতে পাবোঃ

যদিও এখানে জাস্ট একটা কিছু রান করে দেখলাম, কিন্তু এই ইভেন্ট অনেক কাজের হতে পারে। আমরা চাইলে এখান থেকে ইনপুট ফিল্ডের ভ্যালুটাও নিতে পারবো। সেক্ষেত্রে কেউ আমাদের ওয়েব অ্যাপ্লিকেশনে ফর্ম এ কাজ করলে আমরা রিয়েল টাইম ডাটা আপডেট করতে পারবো। আগের ইনপুট ফিল্ডের চেঞ্জ ইভেন্টটা একটু মডিফাই করে দিবোঃ

inputs[0].addEventListener('change', function(event) {
console.log(event.target.value);
});

এখানে আমরা event প্যারামিটারের সাহায্যে টার্গেটেড ইলিমেন্ট এর ভ্যালু বা এখানে ইনপুট ফিল্ডের ভ্যালুটা বের করে এনে সেটা কন্সোলে লগ করেছি। যেমন আমরা যদি আমাদের ফিল্ডে এরকম কিছু লিখিঃ

ব্যাস, এখন যদি অন্যকোথাও ক্লিক করি তাহলে চেঞ্জ ইভেন্ট ঘটবে আর আমাদের কাঙ্ক্ষিত ফলাফল কন্সোলে দেখতে পাবোঃ

ফর্ম সাবমিট ইভেন্টঃ ফর্ম সাবমিটে সাধারণত ব্রাউজারের বাই ডিফল্ট আচরণ থাকে। যেমন কোথায় ফর্মটা সাবিমিট করা হবে, রিকোয়েস্ট টাইপ কি হবে ইত্যাদি ইত্যাদি। যেমন আমাদের ফর্মটায় সাবমিট বাটনে ক্লিক করলে দেখবেন ব্রাউজার আবার লোড হয়েছে, এটা ব্রাউজারের বাই ডিফল্ট আচরণ। আমাদের ফর্মে যেহেতু কিছু(action, method) উল্লেখ নাই, তাই বাই ডিফল্ট এটা বর্তমান পেজেই ফর্মটা সাবমিট করবে এবং একইসাথে GET টাইপের রিকোয়েস্ট ব্যবহার করবে। আর সেজন্যেই আমাদের পেজ রিলোড হতে দেখা যায়। এখন জাভাস্ক্রিপ্ট ব্যবহার করে আমরা ফর্ম ভ্যালিডেশন করতে চাইতে পারি অথবা ফর্মের ডাটাগুলো অ্যাজাক্স রিকোয়েস্টের সাহায্যে আমাদের কাঙ্ক্ষিত জায়গায় পাঠাতে পারি। সেক্ষেত্রে ব্রাউজারের বাই ডিফল্ট আচরণ আমাদের রেগুলার কাজের জন্যে বাঁধা হয়ে দাড়াতে পারে। আমরা খুব সহজেই একটু আগে দেখানো উপায়ে সে বাই ডিফল্ট আচরণ আটকাতে পারিঃ

submitBtn.addEventListener('click', function(event) {
event.preventDefault();
});

এটা লিখে সেইভ করে ব্রাউজার রিলোড করে এখন সাবমিট বাটনে ক্লিক করলে দেখবেন আর বাই ডিফল্ট আচরণ হচ্ছে না। এখন আমরা ফর্ম ভ্যালিডেশন বা অ্যাজাক্স রিকোয়েস্ট টাইপের যেকোনোকিছু করতে পারবো ফর্মের উপর।

যেমন আমি যদি চাই, ইউজার কোনো ফর্মের ইনপুট ফিল্ড খালি রেখেই ফর্ম সাবমিট করতে চায়, তাহলে একটা অ্যালার্ট দেখাবো। তাহলে সাবিমিট বাটনের ক্লিক ইভেন্টটাতেই কিছু কোড অ্যাড করতে হবেঃ

submitBtn.addEventListener('click', function(event) {
event.preventDefault();
if(inputs[0].value === '' || inputs[1].value === '') {
alert('Input Field cannot be Empty!');
}
});

এখন যদি আমরা ফর্মের কোনো একটা ইনপুট ফিল্ড খালি রেখে ফর্মটা সাবমিট করতে চাই তাহলে অ্যালার্ট পাবোঃ

এখন যদি ইনপুট ফিল্ডগুলো দুইটাই ফীল করে বাটনে সাবমিট করি তাহলে আর অ্যালার্ট বক্স আসবে না। এখন আমরা যদি আরো মজার কিছু করতে চাই, যেমন ফীল্ড দুইটাই ফীল থাকলে আমরা ভিতরের ডাটাগুলো দেখাতে চাই। তাহলে সেই সাবমিটের ক্লিক ইভেন্টেই আবার আরো কিছু কোড অ্যাড করতে হবেঃ

submitBtn.addEventListener('click', function(event) {
event.preventDefault();
if(inputs[0].value === '' || inputs[1].value === '') {
alert('Input Field cannot be Empty!');
} else {
var input0Data = inputs[0].value;
var input1Data = inputs[1].value;
console.log('Your Name:', input0Data, 'And Your Email:', input1Data);
}

});

এখন ইনপুট ফীল্ড দুইটাই ফীল করে যদি সাবমিট করি তাহলে আমরা আমাদের ফীল্ডের ডাটাগুলোই দেখতে পাবো কন্সোলেঃ

সাবমিট বাটনে ক্লিক করলে কন্সোলে আউটপুট পাবোঃ

আর কোনো একটা ইনপুট ফীল্ডও যদি খালি থাকে, তাহলে অ্যালার্ট বক্স আসবে।

৭। কী-বোর্ড ইভেন্টঃ কী-বোর্ডের কী চাপলেও কোনো ইভেন্ট ঘটতে পারে, আর সেগুলোও আমরা আগেরমতো করেই হ্যান্ডেল করতে পারবো। এখানে তেমন বিশেষ কিছু নাই। তারপরেও কী-বোর্ডের ইভেন্ট নিয়ে একটু গভীরভাবে আলোচনা করার মতো কিছু ব্যাপার আছে। যেমন আমরা একটা কী তে চাপলে তিনরকমের ইভেন্ট ঘটতে পারে। প্রথমত একটা কী চাপলাম, এখনো কী টা ছাড়ি নাই, সিস্টেম কোনো কী স্ট্রোক রেজিস্টার করে নাই, এইসময়ে একটা ইভেন্ট হতে পারে। তারপর কী চেপে রাখা অবস্থাতেই সিস্টেম কী স্ট্রোক রেজিস্টার করেছে, ঐসময়ে একটা ইভেন্ট হতে পারে। আর একদম শেষে কী চেপে ছেড়ে দিলে তখন আরেকটা ইভেন্ট হতে পারে। এগুলোর উপর ভিত্তি করেই কী-বোর্ডের তিনরকমের ইভেন্ট ঘটতে পারেঃ

  • keydown: প্রথম যে পরিস্থিতির কথা বললাম, কী চাপা হয়েছে কিন্তু সিস্টেম এখনো রেজিস্টার করে নাই। এটা হ্যান্ডেল করার জন্যে keydown ইউজ করা হয়। এখন কী-বোর্ড ইভেন্টগুলো আমরা আমাদের document এর উপর অ্যাপ্লাই করতে হবে যেহেতু এটা পুরো ডকুমেন্ট এর উপরেই থাকবে। বাকী সব আগের মতোইঃ
document.addEventListener('keydown', function() {
console.log('Key Down Event');
});

এখন স্ক্রিপ্ট সেইভ করে ব্রাউজার রিলোড করে আমাদের ডকুমেন্ট সিলেক্ট থাকা অবস্থায় কী-বোর্ডের কোনো কী তে চাপ দিলেই দেখবেন কাঙ্ক্ষিত ফাংশন রান করেছেঃ

  • keypress: এই ইভেন্ট ঠিক সিস্টেমে কী-স্ট্রোক রেজিস্টার করা হওয়া মাত্রই ঘটে। বাকী সব আগের মতোইঃ
document.addEventListener('keypress', function() {
console.log('Key Press Event');
});

স্ক্রিপ্ট সেইভ করে ব্রাউজার রিলোড দিয়ে আমাদের ডকুমেন্ট এ কোনো কী চাপা মাত্রই দেখবেন এটা রান করেছেঃ

  • keyup: কী চাপ দিয়ে ছেড়ে দিলে এই ইভেন্ট ঘটেঃ
document.addEventListener('keyup', function() {
console.log('Key Up Event');
});

স্ক্রিপ্ট সেইভ করে ব্রাউজার রিলোড দিয়ে কোনো কী চেপে ছেড়ে দেওয়া মাত্রই এই ইভেন্ট রান করবেঃ

এদের একটার পর আরেকটার ফলাফল দেখলেই বুঝা যায় কে কখন রান করেছে।

এখন আমরা এখানেও মজার কিছু করতে পারি। যেমন আমরা যদি চাই ঠিক কোন কী-টা চাপা হয়েছে তাহলে সেটাও আমাদের সেই event প্যারামিটারের সাহায্যে অ্যাক্সেস করে শো করাতে পারিঃ

document.addEventListener('keydown', function(event) {
console.log('Key Down Event');
console.log('Pressed Key: ' + event.key);
});

এখন স্ক্রিপ্ট সেইভ করে ব্রাউজার রিলোড দিয়ে ডকুমেন্ট এ যে কী চাপবেন সেটাই কন্সোলে দেখাবেঃ

সেইমভাবে বাকী দুইটার ক্ষেত্রেও কাজ করবে। এভাবে কী ডিটেক্ট করে পরে আরো মজার মজার অনেক কাজ করতে পারবেন খুব সহজেই।

এখানে আমরা হ্যান্ডেলারে প্যারামিটার হিসেবে event ব্যবহার করছি, কিন্তু আমরা চাইলে সেটা খুলেও দেখতে পারি যে এভাবে আমরা কি কি অ্যাক্সেস করতে পারবো সেটা দেখার জন্যে। সেজন্যে কাঙ্ক্ষিত ইলিমেন্ট এর ইভেন্ট হ্যান্ডেলারে এভাবে console.dir() ব্যবহার করে সহজেই দেখতে পারবেন আপনি এভাবে কি কি অ্যাক্সেস করতে পারবেনঃ

document.addEventListener('keydown', function(event) {
console.log('Key Down Event');
console.log('Pressed Key: ' + event.key);
console.dir(event);
});

এখন স্ক্রিপ্ট সেইভ করে ব্রাউজার রিলোড দিয়ে কোনো কী চাপলেই আপনি event অবজেক্ট দেখতে পারবেন কন্সোল থেকেঃ

৮। মাউস ইভেন্টঃ ঠিক কী-বোর্ডের মতো মাউসেরও সেইম টাইপের স্পেশাল ইভেন্ট আছে। যেমন আমরা কোনো ইলিমেন্ট এর উপর মাউস বা পয়েন্টার নিলে একটা ইভেন্ট ঘটতে পারে। আবার সেই ইলিমেন্ট থেকে মাউস সরিয়ে ফেললে আরেকটা ইভেন্ট ঘটতে পারে।

  • mouseover : কোনো ইলিমেন্ট এর উপর মাউস বা পয়েন্টার নিলে এই ইভেন্ট ঘটবে। এরকম ইভেন্ট আমরা প্রথমেই একটা দেখেছিলামঃ
clickMeBtn.onmouseover = function(event) {
console.log('Mouse Over');
}

সেইমটা addEventListener() দিয়েও করা যাবেঃ

clickMeBtn.addEventListener('mouseover', function() {
console.log('On Mouse Over using addEventListener');
});

ব্যাস, এবার স্ক্রিপ্ট ফাইল সেইভ করে ব্রাউজার রিলোড দিয়ে আমাদের বক্স ১ এর বাটনের উপরে মাউস বা পয়েন্টারটা নিলেই এই ইভেন্ট ঘটবেঃ

  • mouseout : এটাই অনেকটা সেইমই, যখন আমরা ইলিমেন্ট থেকে মাউস বা পয়েন্টার সরিয়ে নিবো, তখনি এই ইভেন্ট ঘটবে। ঠিক আগের বাটনেই আমরা এই ইভেন্ট অ্যাড করবোঃ
clickMeBtn.addEventListener('mouseout', function() {
console.log('On Mouse Out');
});

এখন স্ক্রিপ্ট সেইভ করে ব্রাউজার রিলোড দিয়ে আমাদের কাঙ্ক্ষিত বাটনের উপর মাউস নেওয়া মাত্র আগের ইভেন্ট রান করবে, আবার সরিয়ে নিলে এটা রান করবেঃ

৯। ব্রাউজার ইভেন্টঃ আরো কিছু ইভেন্ট আছে যেগুলোকে আমি ব্রাউজারের ইভেন্টই বলবো। যেমন আমাদের পেজ পুরোপুরি লোড হওয়া শেষ হওয়ার পর একটা ইভেন্ট, আবার পেজে মার্কাপ লোড হওয়ার পর একটা ইভেন্ট অথবা একটা ইলিমেন্ট লোড শেষ হওয়ার পর আরেকটা ইভেন্ট, লোড ফেইল হলে সেটার আবার একটা ইভেন্ট। আমরা এ ধরনের ইভেন্টগুলোও সহজেই হ্যান্ডেল করতে পারবো।

পুরোপুরি লোড হওয়ার পর ইভেন্টঃ আমরা হয়তো এরকম চাইতে পারি যে আমাদের পেজের কন্টেন্টগুলো পুরোপুরী লোড হলে পরেই ইউজারকে দেখানো হবে, আর লোডের সময় একটা লোডার দেখাবো। এরকম পরিস্থিতিতে এই load ইভেন্টটা বেশ কাজের। আমাদের ডকুমেন্ট সবকিছু লোড হলে পরেই এই ইভেন্ট ঘটবে। আর আমরা এই ইভেন্টটা window এর উপর অ্যাপ্লাই করবোঃ

window.addEventListener('load', function() {
console.log('Your Page Fully Loaded!');
});

এটা স্ক্রিপ্ট ফাইলে লিখে সেইভ দিয়ে ব্রাউজার রিলোড করলেই দেখবেন এটা রান করছে, যেহেতু আমাদের পেজ লোড হয়ে গেছেঃ

শুধুমাত্র মার্কাপ লোড হওয়ার পরের ইভেন্টঃ অনেকসময় হয়তো আমাদের শুধুমাত্র মার্কাপ লোড হওয়ার পরপরই কোনো অ্যাকশন নিতে হতে পারে। একটু আগে দেখানো load সাধারণত আমাদের পেজে থাকা মার্কাপ, স্টাইলশীট, স্ক্রিপ্টসহ আরো বাকী সব জিনিস লোড হওয়ার পরে ঘটে, যেখানে এই DOMContentLoaded টা সাধারণত মার্কাপ(এইচটিএমএল) লোড হওয়ার পরপরই ঘটবে। আমরা আমাদের স্ক্রিপ্ট ফাইলে সাধারণত আমাদের ডকুমেন্ট এর কিছু ইলিমেন্ট সিলেক্ট করেই সেগুলোর উপর কাজ করি। এখন যদি কোনো কারণে মার্কাপ লোড হওয়ার আগেই আমাদের সেই স্ক্রিপ্ট রান হয়ে যায়, তাহলে কিন্তু আমাদের ইলিমেন্টগুলো পাবে না। সেক্ষেত্রে সমস্যা বা এরর আসতে পারে। তো এরকম পরিস্থিতিতে এই DOMContentLoadedইভেন্ট বেশ কাজে লাগতে পারে। এটা যেহেতু আমাদের ডকুমেন্ট এর ব্যাপার, তাই ইভেন্টটা document এর উপর অ্যাপ্লাই করবোঃ

document.addEventListener('DOMContentLoaded', function() {
console.log('Your Markup is Ready!');
});

স্ক্রিপ্ট লিখে সেইভ করে ব্রাউজার রিলোড দিলে দেখতে পাবেন আগে এটা রান করেছে, আর পরে load এরটা রান করেছেঃ

নির্দিষ্ট কোনো ইলিমেন্ট লোড হওয়ার পরের ইভেন্টঃ আমাদের বক্স ২ এ দুইটা ইমেজ আছে। তারমধ্যে একটার ভ্যালিড সোর্স আছে, আর সেটা শো করছে। আর দ্বিতীয়টায় ইন-ভ্যালিড সোর্স হওয়ায় ভেঙ্গে গেছে। আমরা কন্সোলে হয়তো এতক্ষন এরকম একটা এররও দেখেছি। আমি আমার স্ক্রিনশটে এটা এতোক্ষন অ্যাভয়েড করে গেছি দেখতে খারাপ দেখা যায় বলেঃ

এখন এই ইলিমেন্টগুলোর লোড হওয়া না হওয়ার উপরও দুইরকম ইভেন্ট হতে পারে। আর সেগুলোও আমরা সেইমভাবেই হ্যান্ডেল করতে পারবো। আর তাই প্রথমেই আমরা ইমেজ দুইটা আলাদা আলাদা করে সিলেক্ট করে নেই। প্রথম ইমেজঃ

var img1 = document.querySelector('img[alt=valid-img]');

দ্বিতীয় ইমেজঃ

var img2 = document.querySelector('img[alt=invalid-img]');
  • ইলিমেন্ট লোড হওয়ার পরঃ আমরা আমাদের উপরের img1 সফলভাবে লোড হওয়ার পর কোনো অ্যাকশন নিতে চাইলেঃ
img1.addEventListener('load', function() {
console.log('Image 1 Has Successfully Loaded!');
});

এখানে ইভেন্ট গুলোর একটা সিকোয়েন্সও দেখতে পাবেন, সেই সাথে আমাদের ইমেজ লোড হওয়ার পরের অ্যাকশনও পাবেনঃ

  • ইলিমেন্ট লোড না হতে পারলেঃ আমাদের দ্বিতীয় ইমেজে ভুল সোর্স দেওয়াতে ইমেজটা লোড হয়নি। এখন এরকম পরিস্থিতিতেও আরেকটা এরর টাইপের ইভেন্ট ঘটতে পারে, আর আমরা সেটার উপরও অ্যাকশন নিতে পারবোঃ
img2.addEventListener('error', function() {
console.log('Image Cannot be loaded!');
});

এখন স্ক্রিপ্ট সেইভ করে ব্রাউজার লোড দিলে অ্যাকশনটা দেখতে পাবেনঃ

পুরোনো ব্রাউজারের সমস্যা

আপনার ওয়েব অ্যাপ্লিকেশনের যদি ইন্টারনেট এক্সপ্লোরার ৮, ৭ অথবা এমন পুরোনো কোনো ব্রাউজারের সাপোর্টও লাগে, তাহলে একটু আলাদাকরেই সবকিছু ভাবতে হবে। কারণ ঐ ভার্শনের ব্রাউজারে অনেককিছুই সাপোর্ট করে না। তারমধ্যে আমাদের এখানে ব্যবহার করা addEventListener() ও অন্যতম। তাই এটা একটু পরিবর্তন করে দিতে হবে আপনাকে যদি ঐরকম পুরোনো ব্রাউজারের জন্যে সাপোর্ট অ্যাড করাই লাগেঃ

yourElement.attachEvent('onclick', function() {
// যা করতে চাচ্ছেন এখানে যাবে
});

প্রধান যে যে পার্থক্যগুলো আমরা এখানে পাবোঃ

  1. আমাদের attachEvent ব্যবহার করতে হবেaddEventListener এর পরবর্তিতে।
  2. বেশীরভাগ ক্ষেত্রেও ইভেন্ট এর নামগুলো on প্রি-ফিক্স দিয়ে শুরু হবে।

এছাড়াও আরো অনেককিছুতেই একটু পরিবর্তন আনা লাগবে, আশা করি এখানে আমরা ডমের সাথে কিভাবে কাজ করছি সেগুলো বুঝতে পারলেই বাকীগুলোও নিজে নিজে করে ফেলতে পারবেন। যেমন এই ওয়েবসাইট[https://caniuse.com] থেকে দেখতে পারবেন আপনি কোন মেথড কোন কোন ব্রাউজারের জন্যে ব্যবহার করতে পারবেন।

আমার এই লেখা পূর্বে আমার ব্লগে প্রকাশিত হয়েছে। চাইলে আমার ব্লগ থেকে ঘুরে আসতে পারেন। ব্লগ থেকে সাবস্ক্রাইব করলে আমি নিজে থেকেই আমার নতুন লেখাগুলো আপনার ইমেইলে প্রতি শুক্রবার সকালে পাঠিয়ে দিবো। ভালো থাকবেন। হ্যাপী প্রোগ্রামিং!

--

--

Zonayed Ahmed
জুনায়েদের ডায়েরী

Front End Engineer — Passionate Programmer — ❤️ JavaScript — Skill is My Weapon, Perfection is My Habit — 🌍https://www.zonayed.me