الجزء الحادي عشر – Events & Event Listeners في JavaScript – سلسلة FSWD – JS

🟡 أولًا: أنواع الأحداث
الأحداث في JavaScript هي التفاعلات أو التغييرات اللي بتحصل على الصفحة. JavaScript بتتيح لك التفاعل مع مجموعة من الأحداث المتنوعة زي:
-
click: عند الضغط على عنصر.
-
mouseover: عندما يتحرك المؤشر فوق عنصر.
-
mouseout: عندما يخرج المؤشر من عنصر.
-
keydown و keyup: عند الضغط أو رفع اليد عن مفتاح من لوحة المفاتيح.
-
input: عند تغيير قيمة حقل إدخال.
-
submit: عند إرسال نموذج.
-
resize: عندما يتم تغيير حجم نافذة المتصفح.
-
focus و blur: عند ترك أو إدخال عنصر مُركّز.
✅ مثال
<button id="clickBtn">اضغط هنا</button>
<input type="text" id="nameInput" placeholder="أدخل اسمك">
<script>
// حدث click
let btn = document.getElementById("clickBtn");
btn.addEventListener("click", function() {
alert("تم الضغط على الزر!");
});
// حدث input
let inputField = document.getElementById("nameInput");
inputField.addEventListener("input", function() {
console.log("قيمة الحقل:", inputField.value);
});
</script>
🟡 ثانيًا: this
keyword في الأحداث
الـ this
في JavaScript بتستخدم للإشارة إلى العنصر الذي تم عليه الحدث.
✅ مثال 1: في الدوال العادية
<button id="clickBtn">اضغط هنا</button>
<script>
let btn = document.getElementById("clickBtn");
btn.addEventListener("click", function() {
console.log(this); // هذا سيشير إلى الزر الذي تم عليه الضغط
});
</script>
الـ this
في المثال ده بتشير للزر الذي تم الضغط عليه.
✅ مثال 2: باستخدام Arrow Functions
في الدوال السهمية (Arrow Functions)، الـ this
لا يشير إلى العنصر الذي حدث عليه الحدث، بل يشير إلى الـ محیط التنفيذ (Execution Context) اللي تم فيه تعريف الـ Arrow Function
<button id="clickBtn">اضغط هنا</button>
<script>
let btn = document.getElementById("clickBtn");
btn.addEventListener("click", () => {
console.log(this); // في Arrow Function, this لا يشير إلى الزر
});
</script>
في الحالة دي، الـ this
مش هيشير للزر لأن الـ Arrow Function
بتحتفظ بـ this
من السياق الخارجي لها.
🟡 ثالثًا: Event Bubbling و Event Delegation
✅ Event Bubbling
Event Bubbling هو مفهوم يعني أن الحدث الذي يحدث على عنصر معين يمر عبر جميع العناصر الأب له في DOM.
مثلاً، لو كان عندك حدث click على عنصر داخل آخر، فإن الحدث سيصل أولاً للعنصر الذي حدث عليه، ثم ينتقل للأب الأعلى وهكذا حتى يصل إلى الـ document في النهاية.
✅ مثال على Event Bubbling
<div id="outerDiv" style="width: 300px; height: 300px; background-color: lightblue;">
<button id="innerButton">اضغط هنا</button>
</div>
<script>
let outerDiv = document.getElementById("outerDiv");
let innerButton = document.getElementById("innerButton");
// حدث على الزر
innerButton.addEventListener("click", function() {
alert("تم الضغط على الزر!");
});
// حدث على العنصر الأب
outerDiv.addEventListener("click", function() {
alert("تم الضغط داخل div!");
});
</script>
في المثال ده، لما تضغط على الزر، الحدث سيصل أولاً للزر، ثم ينتقل للأب outerDiv
. مفهوم bubbling يعني أن الحدث “يتصاعد” من العنصر الداخلي إلى الأعلى.
✅ Event Delegation
Event Delegation هو أسلوب لتقليل التكرار في الكود باستخدام الـ Event Bubbling. بدل ما تضيف مستمع حدث (event listener
) على كل عنصر فرعي، ممكن تضيفه على العنصر الأب وتستفيد من Bubbling.
✅ مثال على Event Delegation:
<div id="parentDiv">
<button class="childBtn">زر 1</button>
<button class="childBtn">زر 2</button>
<button class="childBtn">زر 3</button>
</div>
<script>
let parentDiv = document.getElementById("parentDiv");
// إضافة حدث على العنصر الأب
parentDiv.addEventListener("click", function(e) {
// إذا تم الضغط على زر من الأزرار الفرعية
if (e.target && e.target.classList.contains("childBtn")) {
alert("تم الضغط على الزر: " + e.target.textContent);
}
});
</script>
هنا، احنا ضفنا حدث الـ click
على العنصر الأب parentDiv
، وبعدها تحققنا إذا كان العنصر الذي تم الضغط عليه هو أحد الأزرار الفرعية عن طريق e.target
. كده هنقدر نضيف حدث واحد لجميع الأزرار بدل ما نضيف event listener
لكل زر على حدة.
✅ 💯 مثال كامل
<!DOCTYPE html>
<html lang="ar">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Events Example</title>
</head>
<body>
<h1>تعامل مع الأحداث في JavaScript</h1>
<!-- عناصر HTML -->
<button id="clickBtn">اضغط هنا</button>
<input type="text" id="nameInput" placeholder="أدخل اسمك">
<!-- عناصر لـ Event Bubbling & Event Delegation -->
<div id="parentDiv">
<button class="childBtn">زر 1</button>
<button class="childBtn">زر 2</button>
<button class="childBtn">زر 3</button>
</div>
<!-- سكربت JavaScript -->
<script>
// 1. التعامل مع حدث click
let btn = document.getElementById("clickBtn");
btn.addEventListener("click", function() {
alert("تم الضغط على الزر!");
});
// 2. التعامل مع حدث input
let inputField = document.getElementById("nameInput");
inputField.addEventListener("input", function() {
console.log("قيمة الحقل:", inputField.value);
});
// 3. استخدام Event Bubbling
let parentDiv = document.getElementById("parentDiv");
parentDiv.addEventListener("click", function(e) {
if (e.target && e.target.classList.contains("childBtn")) {
alert("تم الضغط على الزر: " + e.target.textContent);
}
});
</script>
</body>
</html>
✅ ملخص النقاط:
-
أنواع الأحداث:
-
click
,mouseover
,input
,keydown
وغيرها.
-
-
this
keyword:-
يشير إلى العنصر الذي حدث عليه الحدث في الدوال التقليدية.
-
في الـ Arrow Functions،
this
يشير إلى السياق الخارجي.
-
-
Event Bubbling:
-
الحدث يتصاعد من العنصر الذي حدث عليه إلى العناصر الأب.
-
-
Event Delegation:
-
بدلاً من إضافة مستمع حدث لكل عنصر، يمكن إضافة مستمع حدث واحد على العنصر الأب.
-
✅ نطاق المتغيرات (Scope) في JavaScript
نطاق المتغيرات هو المكان أو النطاق اللي بيتم فيه تعريف المتغيرات، وده بيحدد مكان الوصول أو التعديل عليها في الكود.
🟡 أولًا: Global Scope vs Local Scope
✅ Global Scope (النطاق العالمي)
أي متغير يتم تعريفه خارج أي دالة أو كتلة تعليمات (block) بيكون في النطاق العالمي. المتغيرات في النطاق العالمي يمكن الوصول إليها من أي مكان في الكود.
✅ Local Scope (النطاق المحلي)
أي متغير يتم تعريفه داخل دالة أو داخل كتلة (مثل if
أو for
) بيكون في النطاق المحلي، وبالتالي لا يمكن الوصول إليه إلا داخل هذه الدالة أو الكتلة.
✅ مثال على Global Scope و Local Scope:
// متغير في النطاق العالمي
let globalVar = "أنا في النطاق العالمي";
function testScope() {
// متغير في النطاق المحلي
let localVar = "أنا في النطاق المحلي";
console.log(globalVar); // ممكن الوصول للمتغير العالمي
console.log(localVar); // ممكن الوصول للمتغير المحلي
}
testScope();
console.log(globalVar); // ممكن الوصول للمتغير العالمي
console.log(localVar); // هنا هتحصل على خطأ لأن localVar معرف فقط داخل الدالة
✅ النتيجة:
-
الكود هيطبع
globalVar
من داخل الدالة لأنها في النطاق العالمي. -
لكن لما تحاول الوصول لـ
localVar
خارج الدالة، هتحصل على خطأ لأن المتغير ده موجود فقط داخل الدالة.
🟡 ثانيًا: Hoisting في JavaScript
Hoisting هو مفهوم بيحصل فيه “رفع” أو “تحريك” تعريف المتغيرات والدوال في JavaScript. يعني، الكود بيتنفذ بشكل صاعد، وبالتالي بيتم رفع المتغيرات والدوال قبل تنفيذ الكود.
✅ كيف يعمل Hoisting؟
-
المتغيرات المعلنة باستخدام
var
يتم رفع تعريف المتغير فقط، مش القيمة. -
الدوال يتم رفع التعريف والتنفيذ بالكامل.
✅ مثال 1: Hoisting مع var
console.log(myVar); // undefined
var myVar = "Hello";
console.log(myVar); // "Hello"
✅ شرح:
-
في السطر الأول، عندما حاولنا الوصول إلى
myVar
قبل أن يتم تعريفه، الطباعة كانتundefined
لأنه تم رفع التعريف فقط بدون القيمة. -
بعد التعريف، أصبح
myVar
يحتوي على"Hello"
.
✅ مثال 2: Hoisting مع الدوال (Function Hoisting)
greet(); // "Hello!"
function greet() {
console.log("Hello!");
}
✅ شرح:
-
الدالة
greet()
تم رفعها بالكامل، وبالتالي يمكن استدعاء الدالة قبل تعريفها في الكود.
🟡 ثالثًا: Hoisting مع let
و const
في حال استخدام let
و const
، عملية hoisting لا تعمل بنفس الطريقة كما في var
. بالرغم من أن تعريف المتغيرات يتم رفعه، لكن القيمة لا تكون موجودة، وبالتالي إذا حاولت الوصول إليها قبل تعريفها، هتحصل على خطأ ReferenceError.
✅ مثال مع let
و const
:
console.log(myVar); // ReferenceError: Cannot access 'myVar' before initialization
let myVar = "Hello";
✅ شرح:
-
هنا، يتم رفع
myVar
ولكن لا يمكن الوصول إليها قبل تعريفها، وبالتالي يحدث خطأ.
✅ 💯 مثال كامل على Scope و Hoisting:
<!DOCTYPE html>
<html lang="ar">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Scope and Hoisting Example</title>
</head>
<body>
<script>
// 1. Global Scope
let globalVar = "أنا متغير في النطاق العالمي";
function testScope() {
// 2. Local Scope
let localVar = "أنا متغير في النطاق المحلي";
console.log(globalVar); // الوصول للمتغير في النطاق العالمي
console.log(localVar); // الوصول للمتغير في النطاق المحلي
}
testScope();
console.log(globalVar); // ممكن الوصول للمتغير العالمي
// console.log(localVar); // هتحصل على خطأ هنا لأن localVar معرف فقط داخل الدالة
// 3. Hoisting Example with var
console.log(myVar); // undefined
var myVar = "Hello from var";
console.log(myVar); // "Hello from var"
// 4. Hoisting Example with function
greet(); // "Hello, world!"
function greet() {
console.log("Hello, world!");
}
// 5. Hoisting Example with let
// console.log(myLet); // ReferenceError: Cannot access 'myLet' before initialization
let myLet = "Hello from let";
// 6. Hoisting Example with const
// console.log(myConst); // ReferenceError: Cannot access 'myConst' before initialization
const myConst = "Hello from const";
</script>
</body>
</html>
✅ شرح الكود:
-
Global Scope:
globalVar
معرف في النطاق العالمي، وبالتالي يمكن الوصول إليه من أي مكان في الكود. -
Local Scope:
localVar
معرف داخل الدالةtestScope()
، وبالتالي لا يمكن الوصول إليه خارج الدالة. -
Hoisting مع
var
: عند الوصول إلىmyVar
قبل تعريفه، الطباعة كانتundefined
لأنvar
بيتم رفع التعريف فقط. -
Hoisting مع دوال: الدالة
greet()
يتم رفعها بالكامل، وبالتالي يمكن استدعاء الدالة قبل تعريفها في الكود. -
Hoisting مع
let
وconst
: معlet
وconst
، عند محاولة الوصول للمتغير قبل تعريفه يحدث خطأ ReferenceError.
✅ ملخص النقاط:
-
Global Scope:
-
المتغيرات المعرّفة في النطاق العالمي يمكن الوصول إليها من أي مكان في الكود.
-
-
Local Scope:
-
المتغيرات المعرّفة داخل دالة أو كتلة لا يمكن الوصول إليها إلا داخل تلك الدالة أو الكتلة.
-
-
Hoisting:
-
var
يتم رفعه بشكل جزئي (التعريف فقط). -
الدوال يتم رفعها بالكامل (التعريف والتنفيذ).
-
let
وconst
يتم رفع تعريف المتغير فقط، ولكن لا يمكن الوصول إليه قبل تعريفه، مما يؤدي إلى ReferenceError.
-
💰 هل تبحث عن طريقة سهلة للربح من الإنترنت؟
ابدأ الآن واكسب أموالًا حقيقية من خلال خطوات بسيطة! 🌟
اضغط وابدأ الربح