يسمح لنا التقاط والتدفق بتنفيذ أحد أنماط معالجة الأحداث الأكثر قوة المسمى تفويض الحدث.
الفكرة هي أنه إذا كان لدينا الكثير من العناصر المعالجة بطريقة مماثلة ، فبدلاً من تعيين معالج لكل منها – نضع معالجًا واحدًا على سلفهم المشترك.
في المعالج نحصل على event.target
لمعرفة المكان الذي حدث فيه الحدث فعليًا ومعالجته.
لنرى مثالًا – رسم با-غوا يعكس الفلسفة الصينية القديمة.
ها هو:
HTML مثل هذا:
<table>
<tr>
<th colspan="3">جدول <em>Bagua</em>: الاتجاه ، العنصر ، اللون ، المعنى</th>
</tr>
<tr>
<td class="nw"><strong>الشمال الغربي</strong><br>معدن<br>فضة<br>كبار السن</td>
<td class="n">...</td>
<td class="ne">...</td>
</tr>
<tr>...2 سطرين إضافيين من هذا النوع ...</tr>
<tr>...2 سطرين إضافيين من هذا النوع ...</tr>
</table>
الجدول يحتوي على 9 خلايا ، لكن يمكن أن يكون هناك 99 أو 9999 ، لا يهم.
مهمتنا هي تسليط الضوء على الخلية <td>
عند النقر.
بدلاً من تعيين معالج onclick
لكل <td>
(يمكن أن يكون كثيرًا) – سنقوم بإعداد المعالج “الشامل” على عنصر <table>
.
سيستخدم event.target
للحصول على العنصر المُنقر عليه وتسليط الضوء عليه.
الشفرة:
let selectedTd;
table.onclick = function(event) {
let target = event.target; // أين كان النقر؟
if (target.tagName != 'TD') return; // ليس على TD؟ إذن لسنا مهتمين
highlight(target); // تسليط الضوء عليه
};
function highlight(td) {
if (selectedTd) { // إزالة التسليط الحالي إن وجد
selectedTd.classList.remove('highlight');
}
selectedTd = td;
selectedTd.classList.add('highlight'); // تسليط الضوء على td الجديد
}
لا يهتم هذا الكود بعدد الخلايا الموجودة في الجدول. يمكننا إضافة / إزالة <td>
ديناميكيًا في أي وقت وسيظل التسليط يعمل.
مع ذلك ، هناك عيب.
قد يحدث النقر ليس على <td>
، ولكن داخله.
في حالتنا إذا نظرنا داخل HTML ، يمكننا رؤية علامات متداخلة داخل <td>
، مثل <strong>
:
<td>
<strong>الشمال الغربي</strong>
...
</td>
بطبيعة الحال ، إذا حدث نقر على هذا <strong>
فإنه يصبح قيمة event.target
.
في المعالج table.onclick
يجب أن نأخذ هذا event.target
ونعرف ما إذا كان النقر داخل <td>
أم لا.
هذه هي الشفرة المحسنة:
table.onclick = function(event) {
let td = event.target.closest('td'); // (1)
if (!td) return; // (2)
if (!table.contains(td)) return; // (3)
highlight(td); // (4)
};
تفسيرات:
- يعيد الأسلوب
elem.closest(selector)
السلف الأقرب الذي يطابق المحدد. في حالتنا نبحث عن<td>
في الطريق لأعلى من العنصر المصدر. - إذا لم يكن
event.target
داخل أي<td>
، فإن الاستدعاء يعود فورًا ، لأنه لا يوجد شيء يمكن القيام به. - في حالة الجداول المتداخلة ، قد يكون
event.target
هو<td>
، ولكن يكمن خارج الجدول الحالي. لذلك نتحقق مما إذا كان هذا هو<td>
لجدولنا فعلاً. - وإذا كان كذلك ، فتسلط الضوء عليه.
وبالتالي ، لدينا رمز تسليط سريع وفعال ، لا يهتم بإجمالي عدد <td>
في الجدول.
مثال التفويض: الإجراءات في الترميز
هناك استخدامات أخرى لتفويض الحدث.
لنقل ، نريد عمل قائمة بأزرار “حفظ” و “تحميل” و “بحث” وما إلى ذلك. وهناك كائن به طرق save
و load
و search
… كيفية مطابقتها؟
قد تكون الفكرة الأولى هي تعيين معالج منفصل لكل زر. لكن هناك حلاً أكثر أناقة. يمكننا إضافة معالج للقائمة بأكملها وسمات data-action
للأزرار التي لديها الطريقة المطلوب استدعاؤها:
<button data-action="save">انقر للحفظ</button>
يقرأ المعالج السمة وينفذ الطريقة. ألق نظرة على المثال العامل:
<div id="menu">
<button data-action="save">حفظ</button>
<button data-action="load">تحميل</button>
<button data-action="search">بحث</button>
</div>
<script>
class Menu {
constructor(elem) {
this._elem = elem;
elem.onclick = this.onClick.bind(this); // (*)
}
save() {
alert('جاري الحفظ');
}
load() {
alert('جاري التحميل');
}
search() {
alert('جاري البحث');
}
onClick(event) {
let action = event.target.dataset.action;
if (action) {
this[action]();
}
};
}
new Menu(menu);
</script>
يرجى ملاحظة أن this.onClick
مرتبط بـ this
في (*)
. هذا مهم ، لأنه وإلا فإن this
داخله سيشير إلى عنصر DOM (elem
) ، وليس كائن Menu
، ولن يكون this[action]
هو ما نحتاجه.
إذن ، ما هي المزايا التي يمنحنا التفويض هنا؟
- ليس علينا كتابة الكود لتعيين معالج لكل زر. فقط قم بعمل طريقة وضعها في الترميز.
- هيكل HTML مرن ، يمكننا إضافة / إزالة الأزرار في أي وقت.
يمكننا أيضًا استخدام الفئات .action-save
و .action-load
، ولكن السمة data-action
هي أفضل من الناحية الدلالية. ويمكننا استخدامه في قواعد CSS أيضًا.
نمط “السلوك”
يمكننا أيضًا استخدام تفويض الحدث لإضافة “سلوكيات” إلى العناصر بشكل تعريفي ، مع سمات وفئات خاصة.
النمط له جزأين:
- نضيف سمة مخصصة إلى عنصر يصف سلوكه.
- يتتبع معالج على مستوى المستند الأحداث ، وإذا حدث حدث على عنصر مسمى – يؤدي الإجراء.
السلوك: العداد
على سبيل المثال ، هنا تضيف السمة data-counter
سلوكًا: “زيادة القيمة عند النقر” على الأزرار:
العداد: <input type="button" value="1" data-counter>
عداد آخر: <input type="button" value="2" data-counter>
<script>
document.addEventListener('click', function(event) {
if (event.target.dataset.counter != undefined) { // إذا كانت السمة موجودة ...
event.target.value++;
}
});
</script>
إذا قمنا بالنقر على زر – يتم زيادة قيمته. ليس الأزرار ، ولكن النهج العام مهم هنا.
يمكن أن يكون هناك العديد من السمات مع data-counter
كما نريد. يمكننا إضافة جديدة إلى HTML في أي لحظة. باستخدام تفويض الحدث ، “قمنا بتوسيع” HTML ، وأضفنا سمة تصف سلوكًا جديدًا.
addEventListener
عندما نعين معالج حدث على كائن document
، يجب علينا دائمًا استخدام addEventListener
، وليس <document.on<event
، لأن الأخير سيسبب تعارضات: المعالجات الجديدة تكتب فوق القديمة.
بالنسبة للمشاريع الحقيقية ، فمن الطبيعي أن يكون هناك العديد من المعالجات على document
التي تم تعيينها بواسطة أجزاء مختلفة من الكود.
السلوك: المفتاح
مثال آخر على السلوك. سيعرض / يخفي النقر على عنصر بسمة data-toggle-id
العنصر بالمعرف المعطى:
<button data-toggle-id="subscribe-mail">
أظهر نموذج الاشتراك
</button>
<form id="subscribe-mail" hidden>
بريدك: <input type="email">
</form>
<script>
document.addEventListener('click', function(event) {
let id = event.target.dataset.toggleId;
if (!id) return;
let elem = document.getElementById(id);
elem.hidden = !elem.hidden;
});
</script>
لنلاحظ مرة أخرى ما فعلناه. الآن ، لإضافة وظيفة التبديل إلى عنصر – لا حاجة لمعرفة JavaScript ، فقط استخدم السمة data-toggle-id
.
قد يصبح ذلك مريحًا حقًا – لا حاجة لكتابة JavaScript لكل عنصر من هذا النوع. فقط استخدم السلوك. يجعل المعالج على مستوى المستند عمله لأي عنصر في الصفحة.
يمكننا دمج سلوكيات متعددة على عنصر واحد أيضًا.
يمكن أن يكون نمط “السلوك” بديلاً للشظايا الصغيرة من JavaScript.
ملخص
تفويض الحدث رائع حقًا! إنه واحد من أكثر الأنماط المفيدة لأحداث DOM.
غالبًا ما يتم استخدامه لإضافة نفس المعالجة للعديد من العناصر المتشابهة ، ولكن ليس فقط لذلك.
الخوارزمية:
- ضع معالج واحد على الحاوية.
- في المعالج – تحقق من عنصر المصدر
event.target
. - إذا حدث الحدث داخل عنصر يهمنا ، فقم بمعالجة الحدث.
الفوائد:
- يبسط التهيئة ويوفر الذاكرة: لا حاجة لإضافة العديد من المعالجات.
- أقل كود: عند إضافة أو إزالة عناصر ، لا حاجة لإضافة / إزالة المعالجات.
- تعديلات DOM: يمكننا إضافة / إزالة عناصر بكتلة مع
innerHTML
وما شابه.
بالطبع للتفويض قيوده:
- أولاً ، يجب أن يكون الحدث فقاعيًا. بعض الأحداث لا تتدفق. أيضًا ، يجب على المعالجات المنخفضة المستوى عدم استخدام
()event.stopPropagation
. - ثانيًا ، قد يضيف التفويض حملًا على وحدة المعالجة المركزية ، لأن معالج المستوى الحاوية يتفاعل مع الأحداث في أي مكان من الحاوية ، بغض النظر عما إذا كانت تهمنا أم لا. ولكن عادة ما يكون الحمل ضئيلًا ، لذلك لا نأخذه في الاعتبار.
التعليقات
<code>
، وللكثير من السطور استخدم<pre>
، ولأكثر من 10 سطور استخدم (plnkr, JSBin, codepen…)