Lesson 8 / الدرس 8

Bubbling and delegation / الانتشار والتفويض

An event does not stop where it happened — it travels up the tree. That one behaviour lets you attach a single listener instead of two hundred, and it solves the problem lesson 3 warned about.

الحدث لا يتوقف حيث وقع — بل يسافر صاعدًا في الشجرة. وذلك السلوك وحده يتيح لك ربط مستمع واحد بدل مئتين، ويحل المشكلة التي حذّر منها الدرس 3.

An event travels up

You click the button:

  <body>            4. and finally here
    <ul>            3. then here
      <li>          2. then here
        <button>    1. the event starts here

A listener on the <ul> hears clicks on every button inside it.
This is called bubbling, and it is why a listener on a parent fires for children you never attached anything to — including children that did not exist when the listener was added. Run the demonstration.

Why this is the standard pattern

A listener per itemOne listener on the parent
Two hundred rowsTwo hundred listenersOne
Rows added laterNeed attaching individuallyAlready handled
After replaceChildrenAll listeners lostStill there
Removing a rowRemember to detachNothing to do
Reading the codeAttachment scatteredOne place

The third row is the fix for lesson 3's warning. Rebuilding a list destroys every listener inside it, so a page that re-renders needs delegation or it silently stops responding after the first update.

The two lines that make it work are worth reading closely. event.target.closest("[data-action='remove']") starts at whatever was actually clicked and walks up until it finds a remove button — so a click on an icon inside the button still finds it. And the if (!button) return is the guard: without it, a click anywhere else in the list would fall through into code expecting a button.

data- attributes are the usual way to mark what an element does, and they were in the HTML course for this reason. data-action="remove" reads clearly, is available as button.dataset.action in JavaScript, and does not collide with styling the way keying off a class does — someone renaming a CSS class should not break your listeners.

Try it live / جرّب بنفسك

Preview / المعاينة

Check yourself / اختبر نفسك

1. Why does one listener on a list handle items added later?

2. A delegated button works at its edge and not in the middle. Why?

3. Delegating focus does nothing. What is the fix?

Your task / مهمتك

Build a to-do list with add, remove and a "done" toggle, using exactly one click listener on the container for all three actions — distinguished by a data-action attribute. Put an icon or a span inside one of the buttons to prove closest is doing its job. Then rebuild the whole list with replaceChildren and show that everything still works afterwards.

ابنِ قائمة مهام بإضافة وإزالة ومبدّل "منجَز"، مستخدمًا مستمع نقر واحدًا بالضبط على الحاوية للأفعال الثلاثة — مميَّزة بسمة data-action. وضع أيقونة أو span داخل أحد الأزرار لتثبت أن closest يؤدي عمله. ثم أعد بناء القائمة كلها بـreplaceChildren وأظهر أن كل شيء ما يزال يعمل بعدها.

  • Exactly one click listener for three actions مستمع نقر واحد بالضبط لثلاثة أفعال
  • data-action used, not CSS classes data-action مستخدمة لا أصناف CSS
  • A nested element inside a button, still working عنصر متداخل داخل زر وما يزال يعمل
  • Everything still works after a full re-render كل شيء ما يزال يعمل بعد إعادة عرض كاملة
How do you want to submit? / كيف تريد التسليم؟