رویدادها (Events)
Event چیست، Event Listener، رویدادهای ماوس و صفحهکلید و فرم، Bubbling و Capturing، Delegation، preventDefault و stopPropagation، Custom Events و رویدادهای Pointer و Touch.
Event چیست؟
رویداد سیگنالی است که از تعامل کاربر یا سیستم صادر میشود — کلیک، تایپ، اسکرول، لود:
المان / پنجره → رویداد → handler (تابع)
Event Listener — شنونده رویداد
const btn = document.querySelector("#save");
// روش مدرن — addEventListener:
btn.addEventListener("click", function (e) {
console.log("کلیک شد!", e.target);
});
// چند شنونده برای یک رویداد:
btn.addEventListener("click", () => console.log("اول"));
btn.addEventListener("click", () => console.log("دوم"));
// هر دو اجرا میشوند!
// حذف — باید همان مرجع تابع باشد:
function handler(e) { console.log("یک بار"); }
btn.addEventListener("click", handler);
btn.removeEventListener("click", handler);
با
removeEventListener حتماً anonymous function کار نمیکند — مرجع تابع لازم است.شیء Event
btn.addEventListener("click", (e) => {
console.log(e.type); // "click"
console.log(e.target); // منبع واقعی کلیک
console.log(e.currentTarget);// شنونده فعلی
console.log(e.clientX, e.clientY); // مختصات
console.log(e.timeStamp); // زمان از لود صفحه
console.log(e.ctrlKey); // Ctrl فشرده؟
});
رویدادهای ماوس
const box = document.querySelector(".box");
box.addEventListener("click", e => console.log("کلیک"));
box.addEventListener("dblclick", e => console.log("دوبل کلیک"));
box.addEventListener("mousedown", e => console.log("پرس شده"));
box.addEventListener("mouseup", e => console.log("رها شده"));
box.addEventListener("mouseenter", e => console.log("ورود — بدون حباب"));
box.addEventListener("mouseleave", e => console.log("خروج"));
box.addEventListener("mousemove", e => {
// مختصات نسبت به عنصر:
const rect = box.getBoundingClientRect();
console.log(e.clientX - rect.left, e.clientY - rect.top);
});
// contextmenu — کلیک راست:
box.addEventListener("contextmenu", e => {
e.preventDefault(); // منوی پیشفرض نیاید
console.log("منوی سفارشی!");
});
رویدادهای صفحهکلید
document.addEventListener("keydown", (e) => {
console.log(e.key); // "a", "Enter", "Escape"
console.log(e.code); // "KeyA" — فیزیکی
console.log(e.repeat); // نگهداشته و تکرار؟
// میانبر Ctrl+S:
if (e.ctrlKey && e.key === "s") {
e.preventDefault();
console.log("ذخیره!");
}
// فقط عدد:
if (!/^\d$/.test(e.key) && !["Backspace", "Delete", "Arrow"].includes(e.key)) {
e.preventDefault();
}
});
document.addEventListener("keyup", e => console.log("رها شد:", e.key));
document.addEventListener("keypress", e => console.log("کاراکتر:", e.key));
رویدادهای فرم و ورودی
const input = document.querySelector("input");
// input — هر تغییر فوری:
input.addEventListener("input", (e) => {
console.log("مقدار:", e.target.value);
});
// change — بعد از blur اگر عوض شده:
input.addEventListener("change", (e) => {
console.log("تغییر نهایی:", e.target.value);
});
// focus و blur:
input.addEventListener("focus", () => input.classList.add("active"));
input.addEventListener("blur", () => input.classList.remove("active"));
// submit — روی form:
const form = document.querySelector("form");
form.addEventListener("submit", (e) => {
e.preventDefault(); // جلوگیری از رفرش!
console.log("ارسال شد");
});
// reset:
form.addEventListener("reset", () => console.log("ریست"));
Clipboard — کپی و رونوشت
// کپی:
document.querySelector("#copy").addEventListener("click", async () => {
await navigator.clipboard.writeText("متن کپی");
console.log("کپی شد!");
});
// خواندن:
document.querySelector("#paste").addEventListener("click", async () => {
const text = await navigator.clipboard.readText();
console.log("خوانده شد:", text);
});
// رویداد copy/paste:
document.addEventListener("copy", e => {
e.clipboardData.setData("text/plain", "متن سفارشی");
e.preventDefault();
});
Drag & Drop — کشیدن و رها کردن
const draggable = document.querySelector(".draggable");
const dropzone = document.querySelector(".dropzone");
// قابل کشیدن:
draggable.addEventListener("dragstart", (e) => {
e.dataTransfer.setData("text/plain", e.target.id);
e.dataTransfer.effectAllowed = "move";
});
// مقصد:
dropzone.addEventListener("dragover", (e) => {
e.preventDefault(); // لازم برای drop!
dropzone.classList.add("over");
});
dropzone.addEventListener("dragleave", () => {
dropzone.classList.remove("over");
});
dropzone.addEventListener("drop", (e) => {
e.preventDefault();
const id = e.dataTransfer.getData("text/plain");
const el = document.getElementById(id);
dropzone.appendChild(el);
dropzone.classList.remove("over");
});
Touch و Pointer Events
const area = document.querySelector(".touch-area");
// Touch — موبایل:
area.addEventListener("touchstart", (e) => {
console.log("لمسها:", e.touches.length); // تعداد انگشت
console.log("اولین:", e.touches[0].clientX);
});
area.addEventListener("touchmove", (e) => {
const t = e.touches[0];
console.log("حرکت:", t.clientX, t.clientY);
});
// Pointer — ماوس + لمس + قلم همه:
area.addEventListener("pointerdown", (e) => {
console.log(e.pointerType); // "mouse" | "touch" | "pen"
console.log(e.pressure); // فشار
area.setPointerCapture(e.pointerId);
});
area.addEventListener("pointermove", (e) => {
if (e.buttons === 1) { // دکمه چپ فشرده
console.log("کشیدن به:", e.clientX, e.clientY);
}
});
area.addEventListener("pointerup", (e) => {
console.log("پایان");
});
Bubbling و Capturing
رویداد در سه فاز حرکت میکند:
// Capturing: document → ... → target (پایینروی)
// Target: خود عنصر
// Bubbling: target → ... → document (بالاروی)
// grandparent → parent → child:
document.querySelector(".grand").addEventListener("click", () =>
console.log("grand"), true); // true = capturing — اول!
document.querySelector(".parent").addEventListener("click", () =>
console.log("parent")); // bubbling — دوم
document.querySelector(".child").addEventListener("click", (e) => {
console.log("child"); // هدف — سوم (وسط)
// e.stopPropagation(); // جلوگیری از ادامه حباب
});
// کلیک روی child: grand(capture) → child → parent
Event Delegation — نمایندگی
بهجای شنونده برای هر عنصر، یکی روی والد — مقیاسپذیر و سریع:
// بد — ۱۰۰۰ شنونده برای ۱۰۰۰ دکمه ❌
// خوب — ۱ شنونده روی والد ✓
document.querySelector("#list").addEventListener("click", (e) => {
// closest — پیدا کردن دکمه واقعی:
const btn = e.target.closest("button[data-action]");
if (!btn) return;
const action = btn.dataset.action;
const id = btn.dataset.id;
switch (action) {
case "edit": editItem(id); break;
case "delete": deleteItem(id); break;
case "view": viewItem(id); break;
}
});
// عناصر داینامیک هم خودکار پوشش داده میشوند!
preventDefault و stopPropagation
document.querySelector("a").addEventListener("click", (e) => {
e.preventDefault(); // رفتار پیشفرض (Navigation) نباشد
console.log("لینک خنثی شد");
});
document.querySelector(".inner").addEventListener("click", (e) => {
e.stopPropagation(); // به والد نرو
e.preventDefault(); // و رفتار پیشفرض هم نه
console.log("فقط inner");
});
// stopImmediatePropagation — حتی شنوندههای خود عنصر:
btn.addEventListener("click", (e) => {
e.stopImmediatePropagation();
console.log("فقط من!");
});
btn.addEventListener("click", () => console.log("من هرگز!"));
Custom Events — رویداد سفارشی
// ساخت:
const event = new CustomEvent("userLogin", {
detail: { userId: 42, name: "علی" },
bubbles: true
});
// گوش دادن:
document.addEventListener("userLogin", (e) => {
console.log("کاربر وارد شد:", e.detail.name);
});
// پرتاب:
document.dispatchEvent(event);
// کاربرد — ارتباط بین اجزای مستقل:
class Cart extends EventTarget {
#items = [];
add(item) {
this.#items.push(item);
this.dispatchEvent(new CustomEvent("change", {
detail: { count: this.#items.length, item }
}));
}
}
const cart = new Cart();
cart.addEventListener("change", e => {
console.log("سبد آپدیت:", e.detail.count);
});
گزینههای addEventListener
el.addEventListener("click", handler, {
once: true, // فقط یک بار — خودش حذف میشود
passive: true, // به مرورگر بگو preventDefault نمیکنیم — سریعتر برای scroll/touch
capture: true, // فاز capture
signal: controller.signal // AbortController برای حذف
});
// حذف گروهی با AbortController:
const controller = new AbortController();
el.addEventListener("click", h1, { signal: controller.signal });
el.addEventListener("mouseover", h2, { signal: controller.signal });
// همه یکجا حذف:
controller.abort();
جمعبندی
| کار | روش |
|---|---|
| شنیدن | addEventListener(type, fn) |
| عناصر داینامیک | Delegation با closest |
| خنثیسازی پیشفرض | preventDefault |
| قطع حباب | stopPropagation |
| یکبار | { once: true } |
| سفارشی | CustomEvent |
این مبحث بخشی از دوره آموزش کامل جاوا اسکریپت است.