[{"data":1,"prerenderedAt":2724},["ShallowReactive",2],{"page-\u002Fjs\u002F20-events":3},{"id":4,"title":5,"body":6,"description":2717,"extension":2718,"meta":2719,"navigation":122,"path":2720,"seo":2721,"stem":2722,"__hash__":2723},"content\u002Fjs\u002F20-events.md","JavaScript 20 — Event System: Bubbling, Capturing, Delegation & Custom Events",{"type":7,"value":8,"toc":2706},"minimark",[9,14,19,383,387,798,808,1166,1170,1616,1620,1850,1854,2294,2298,2632,2636,2644,2702],[10,11,13],"h1",{"id":12},"_20-event-system-bubbling-capturing-delegation-custom-events","20 — Event System: Bubbling, Capturing, Delegation & Custom Events",[15,16,18],"h2",{"id":17},"event-propagation-capture-target-bubble","Event Propagation: Capture → Target → Bubble",[20,21,23],"code-wrapper",{"language":22},"javascript",[24,25,29],"pre",{"className":26,"code":27,"language":22,"meta":28,"style":28},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F ── Events propagate in 3 phases (DOM event model) ──\n\u002F\u002F\n\u002F\u002F   Capture (top → down): document → ... → parent → target\n\u002F\u002F   Target:               target (the element that was clicked)\n\u002F\u002F   Bubble (bottom → up): target → parent → ... → document\n\u002F\u002F\n\u002F\u002F   \u003Chtml>\n\u002F\u002F     \u003Cbody>\n\u002F\u002F       \u003Cdiv>\n\u002F\u002F         \u003Cbutton>  ← target (click here)\n\u002F\u002F\n\u002F\u002F   Phase 1 (Capture): document → html → body → div → button\n\u002F\u002F   Phase 2 (Target):  button (both capture and bubble fire on target)\n\u002F\u002F   Phase 3 (Bubble):  button → div → body → html → document\n\n\u002F\u002F ── addEventListener: third arg = useCapture (default: false = bubble phase) ──\ndocument.querySelector(\"#parent\").addEventListener(\"click\", (e) => {\n    console.log(\"parent bubble:\", e.target.tagName);\n}, false);  \u002F\u002F bubble phase (default)\n\ndocument.querySelector(\"#parent\").addEventListener(\"click\", (e) => {\n    console.log(\"parent capture:\", e.target.tagName);\n}, true);  \u002F\u002F capture phase (fires FIRST — during top-down)\n\n\u002F\u002F Click on #child inside #parent:\n\u002F\u002F Capture order: document → ... → parent (capture fires) → child\n\u002F\u002F Target order: child (both capture and bubble fire on the target)\n\u002F\u002F Bubble order: child → parent (bubble fires) → ... → document\n\n\u002F\u002F ── e.target vs e.currentTarget ──\ndocument.querySelector(\"#parent\").addEventListener(\"click\", (e) => {\n    console.log(e.target);        \u002F\u002F the ACTUAL element clicked (e.g., #child or a span inside)\n    console.log(e.currentTarget); \u002F\u002F the element with the listener (#parent)\n    \u002F\u002F target: where the event originated (deepest element)\n    \u002F\u002F currentTarget: the element that has the event listener (this in non-arrow)\n});\n","",[30,31,32,41,47,53,59,65,70,76,82,88,94,99,105,111,117,124,130,176,193,209,214,243,257,270,275,281,287,293,299,304,310,339,352,365,371,377],"code",{"__ignoreMap":28},[33,34,37],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sdCPZ","\u002F\u002F ── Events propagate in 3 phases (DOM event model) ──\n",[33,42,44],{"class":35,"line":43},2,[33,45,46],{"class":39},"\u002F\u002F\n",[33,48,50],{"class":35,"line":49},3,[33,51,52],{"class":39},"\u002F\u002F   Capture (top → down): document → ... → parent → target\n",[33,54,56],{"class":35,"line":55},4,[33,57,58],{"class":39},"\u002F\u002F   Target:               target (the element that was clicked)\n",[33,60,62],{"class":35,"line":61},5,[33,63,64],{"class":39},"\u002F\u002F   Bubble (bottom → up): target → parent → ... → document\n",[33,66,68],{"class":35,"line":67},6,[33,69,46],{"class":39},[33,71,73],{"class":35,"line":72},7,[33,74,75],{"class":39},"\u002F\u002F   \u003Chtml>\n",[33,77,79],{"class":35,"line":78},8,[33,80,81],{"class":39},"\u002F\u002F     \u003Cbody>\n",[33,83,85],{"class":35,"line":84},9,[33,86,87],{"class":39},"\u002F\u002F       \u003Cdiv>\n",[33,89,91],{"class":35,"line":90},10,[33,92,93],{"class":39},"\u002F\u002F         \u003Cbutton>  ← target (click here)\n",[33,95,97],{"class":35,"line":96},11,[33,98,46],{"class":39},[33,100,102],{"class":35,"line":101},12,[33,103,104],{"class":39},"\u002F\u002F   Phase 1 (Capture): document → html → body → div → button\n",[33,106,108],{"class":35,"line":107},13,[33,109,110],{"class":39},"\u002F\u002F   Phase 2 (Target):  button (both capture and bubble fire on target)\n",[33,112,114],{"class":35,"line":113},14,[33,115,116],{"class":39},"\u002F\u002F   Phase 3 (Bubble):  button → div → body → html → document\n",[33,118,120],{"class":35,"line":119},15,[33,121,123],{"emptyLinePlaceholder":122},true,"\n",[33,125,127],{"class":35,"line":126},16,[33,128,129],{"class":39},"\u002F\u002F ── addEventListener: third arg = useCapture (default: false = bubble phase) ──\n",[33,131,133,137,141,144,148,151,154,156,159,162,166,169,173],{"class":35,"line":132},17,[33,134,136],{"class":135},"ssxIu","document.",[33,138,140],{"class":139},"sIsaT","querySelector",[33,142,143],{"class":135},"(",[33,145,147],{"class":146},"sJ6F3","\"#parent\"",[33,149,150],{"class":135},").",[33,152,153],{"class":139},"addEventListener",[33,155,143],{"class":135},[33,157,158],{"class":146},"\"click\"",[33,160,161],{"class":135},", (",[33,163,165],{"class":164},"sCrzJ","e",[33,167,168],{"class":135},") ",[33,170,172],{"class":171},"svdQ7","=>",[33,174,175],{"class":135}," {\n",[33,177,179,182,185,187,190],{"class":35,"line":178},18,[33,180,181],{"class":135},"    console.",[33,183,184],{"class":139},"log",[33,186,143],{"class":135},[33,188,189],{"class":146},"\"parent bubble:\"",[33,191,192],{"class":135},", e.target.tagName);\n",[33,194,196,199,203,206],{"class":35,"line":195},19,[33,197,198],{"class":135},"}, ",[33,200,202],{"class":201},"snvgF","false",[33,204,205],{"class":135},");  ",[33,207,208],{"class":39},"\u002F\u002F bubble phase (default)\n",[33,210,212],{"class":35,"line":211},20,[33,213,123],{"emptyLinePlaceholder":122},[33,215,217,219,221,223,225,227,229,231,233,235,237,239,241],{"class":35,"line":216},21,[33,218,136],{"class":135},[33,220,140],{"class":139},[33,222,143],{"class":135},[33,224,147],{"class":146},[33,226,150],{"class":135},[33,228,153],{"class":139},[33,230,143],{"class":135},[33,232,158],{"class":146},[33,234,161],{"class":135},[33,236,165],{"class":164},[33,238,168],{"class":135},[33,240,172],{"class":171},[33,242,175],{"class":135},[33,244,246,248,250,252,255],{"class":35,"line":245},22,[33,247,181],{"class":135},[33,249,184],{"class":139},[33,251,143],{"class":135},[33,253,254],{"class":146},"\"parent capture:\"",[33,256,192],{"class":135},[33,258,260,262,265,267],{"class":35,"line":259},23,[33,261,198],{"class":135},[33,263,264],{"class":201},"true",[33,266,205],{"class":135},[33,268,269],{"class":39},"\u002F\u002F capture phase (fires FIRST — during top-down)\n",[33,271,273],{"class":35,"line":272},24,[33,274,123],{"emptyLinePlaceholder":122},[33,276,278],{"class":35,"line":277},25,[33,279,280],{"class":39},"\u002F\u002F Click on #child inside #parent:\n",[33,282,284],{"class":35,"line":283},26,[33,285,286],{"class":39},"\u002F\u002F Capture order: document → ... → parent (capture fires) → child\n",[33,288,290],{"class":35,"line":289},27,[33,291,292],{"class":39},"\u002F\u002F Target order: child (both capture and bubble fire on the target)\n",[33,294,296],{"class":35,"line":295},28,[33,297,298],{"class":39},"\u002F\u002F Bubble order: child → parent (bubble fires) → ... → document\n",[33,300,302],{"class":35,"line":301},29,[33,303,123],{"emptyLinePlaceholder":122},[33,305,307],{"class":35,"line":306},30,[33,308,309],{"class":39},"\u002F\u002F ── e.target vs e.currentTarget ──\n",[33,311,313,315,317,319,321,323,325,327,329,331,333,335,337],{"class":35,"line":312},31,[33,314,136],{"class":135},[33,316,140],{"class":139},[33,318,143],{"class":135},[33,320,147],{"class":146},[33,322,150],{"class":135},[33,324,153],{"class":139},[33,326,143],{"class":135},[33,328,158],{"class":146},[33,330,161],{"class":135},[33,332,165],{"class":164},[33,334,168],{"class":135},[33,336,172],{"class":171},[33,338,175],{"class":135},[33,340,342,344,346,349],{"class":35,"line":341},32,[33,343,181],{"class":135},[33,345,184],{"class":139},[33,347,348],{"class":135},"(e.target);        ",[33,350,351],{"class":39},"\u002F\u002F the ACTUAL element clicked (e.g., #child or a span inside)\n",[33,353,355,357,359,362],{"class":35,"line":354},33,[33,356,181],{"class":135},[33,358,184],{"class":139},[33,360,361],{"class":135},"(e.currentTarget); ",[33,363,364],{"class":39},"\u002F\u002F the element with the listener (#parent)\n",[33,366,368],{"class":35,"line":367},34,[33,369,370],{"class":39},"    \u002F\u002F target: where the event originated (deepest element)\n",[33,372,374],{"class":35,"line":373},35,[33,375,376],{"class":39},"    \u002F\u002F currentTarget: the element that has the event listener (this in non-arrow)\n",[33,378,380],{"class":35,"line":379},36,[33,381,382],{"class":135},"});\n",[15,384,386],{"id":385},"event-delegation","Event Delegation",[20,388,389],{"language":22},[24,390,392],{"className":26,"code":391,"language":22,"meta":28,"style":28},"\u002F\u002F ── Event delegation: one listener on a parent handles events for all children ──\n\u002F\u002F Instead of N listeners on N items, use 1 listener on the parent + event.target.\n\n\u002F\u002F ❌ NAIVE — one listener per item (memory + re-binding when items change)\ndocument.querySelectorAll(\".item\").forEach(item => {\n    item.addEventListener(\"click\", () => console.log(item.dataset.id));\n});\n\u002F\u002F Adding a new item? Must add a listener to it too.\n\n\u002F\u002F ✅ CORRECT — one listener on parent (event delegation)\ndocument.querySelector(\"#list\").addEventListener(\"click\", (e) => {\n    const item = e.target.closest(\".item\");  \u002F\u002F find the closest .item ancestor\n    if (!item) return;  \u002F\u002F click wasn't on an .item (or descendant)\n    if (!e.currentTarget.contains(item)) return;  \u002F\u002F safety: item is inside the parent\n    console.log(\"clicked:\", item.dataset.id);\n});\n\u002F\u002F Adding a new item? No need to add a listener — the parent's delegation handles it automatically.\n\n\u002F\u002F ── Delegation with data attributes for actions ──\ndocument.querySelector(\"#app\").addEventListener(\"click\", (e) => {\n    const action = e.target.closest(\"[data-action]\");\n    if (!action) return;\n\n    switch (action.dataset.action) {\n        case \"delete\":\n            const id = action.dataset.id;\n            deleteItem(id);\n            break;\n        case \"edit\":\n            editItem(action.dataset.id);\n            break;\n        case \"save\":\n            saveForm(action.closest(\"form\"));\n            break;\n    }\n});\n",[30,393,394,399,404,408,413,440,464,468,473,477,482,511,537,560,584,598,602,607,611,616,645,666,682,686,694,705,718,726,733,742,750,756,765,783,789,794],{"__ignoreMap":28},[33,395,396],{"class":35,"line":36},[33,397,398],{"class":39},"\u002F\u002F ── Event delegation: one listener on a parent handles events for all children ──\n",[33,400,401],{"class":35,"line":43},[33,402,403],{"class":39},"\u002F\u002F Instead of N listeners on N items, use 1 listener on the parent + event.target.\n",[33,405,406],{"class":35,"line":49},[33,407,123],{"emptyLinePlaceholder":122},[33,409,410],{"class":35,"line":55},[33,411,412],{"class":39},"\u002F\u002F ❌ NAIVE — one listener per item (memory + re-binding when items change)\n",[33,414,415,417,420,422,425,427,430,432,435,438],{"class":35,"line":61},[33,416,136],{"class":135},[33,418,419],{"class":139},"querySelectorAll",[33,421,143],{"class":135},[33,423,424],{"class":146},"\".item\"",[33,426,150],{"class":135},[33,428,429],{"class":139},"forEach",[33,431,143],{"class":135},[33,433,434],{"class":164},"item",[33,436,437],{"class":171}," =>",[33,439,175],{"class":135},[33,441,442,445,447,449,451,454,456,459,461],{"class":35,"line":67},[33,443,444],{"class":135},"    item.",[33,446,153],{"class":139},[33,448,143],{"class":135},[33,450,158],{"class":146},[33,452,453],{"class":135},", () ",[33,455,172],{"class":171},[33,457,458],{"class":135}," console.",[33,460,184],{"class":139},[33,462,463],{"class":135},"(item.dataset.id));\n",[33,465,466],{"class":35,"line":72},[33,467,382],{"class":135},[33,469,470],{"class":35,"line":78},[33,471,472],{"class":39},"\u002F\u002F Adding a new item? Must add a listener to it too.\n",[33,474,475],{"class":35,"line":84},[33,476,123],{"emptyLinePlaceholder":122},[33,478,479],{"class":35,"line":90},[33,480,481],{"class":39},"\u002F\u002F ✅ CORRECT — one listener on parent (event delegation)\n",[33,483,484,486,488,490,493,495,497,499,501,503,505,507,509],{"class":35,"line":96},[33,485,136],{"class":135},[33,487,140],{"class":139},[33,489,143],{"class":135},[33,491,492],{"class":146},"\"#list\"",[33,494,150],{"class":135},[33,496,153],{"class":139},[33,498,143],{"class":135},[33,500,158],{"class":146},[33,502,161],{"class":135},[33,504,165],{"class":164},[33,506,168],{"class":135},[33,508,172],{"class":171},[33,510,175],{"class":135},[33,512,513,516,519,522,525,528,530,532,534],{"class":35,"line":101},[33,514,515],{"class":171},"    const",[33,517,518],{"class":201}," item",[33,520,521],{"class":171}," =",[33,523,524],{"class":135}," e.target.",[33,526,527],{"class":139},"closest",[33,529,143],{"class":135},[33,531,424],{"class":146},[33,533,205],{"class":135},[33,535,536],{"class":39},"\u002F\u002F find the closest .item ancestor\n",[33,538,539,542,545,548,551,554,557],{"class":35,"line":107},[33,540,541],{"class":171},"    if",[33,543,544],{"class":135}," (",[33,546,547],{"class":171},"!",[33,549,550],{"class":135},"item) ",[33,552,553],{"class":171},"return",[33,555,556],{"class":135},";  ",[33,558,559],{"class":39},"\u002F\u002F click wasn't on an .item (or descendant)\n",[33,561,562,564,566,568,571,574,577,579,581],{"class":35,"line":113},[33,563,541],{"class":171},[33,565,544],{"class":135},[33,567,547],{"class":171},[33,569,570],{"class":135},"e.currentTarget.",[33,572,573],{"class":139},"contains",[33,575,576],{"class":135},"(item)) ",[33,578,553],{"class":171},[33,580,556],{"class":135},[33,582,583],{"class":39},"\u002F\u002F safety: item is inside the parent\n",[33,585,586,588,590,592,595],{"class":35,"line":119},[33,587,181],{"class":135},[33,589,184],{"class":139},[33,591,143],{"class":135},[33,593,594],{"class":146},"\"clicked:\"",[33,596,597],{"class":135},", item.dataset.id);\n",[33,599,600],{"class":35,"line":126},[33,601,382],{"class":135},[33,603,604],{"class":35,"line":132},[33,605,606],{"class":39},"\u002F\u002F Adding a new item? No need to add a listener — the parent's delegation handles it automatically.\n",[33,608,609],{"class":35,"line":178},[33,610,123],{"emptyLinePlaceholder":122},[33,612,613],{"class":35,"line":195},[33,614,615],{"class":39},"\u002F\u002F ── Delegation with data attributes for actions ──\n",[33,617,618,620,622,624,627,629,631,633,635,637,639,641,643],{"class":35,"line":211},[33,619,136],{"class":135},[33,621,140],{"class":139},[33,623,143],{"class":135},[33,625,626],{"class":146},"\"#app\"",[33,628,150],{"class":135},[33,630,153],{"class":139},[33,632,143],{"class":135},[33,634,158],{"class":146},[33,636,161],{"class":135},[33,638,165],{"class":164},[33,640,168],{"class":135},[33,642,172],{"class":171},[33,644,175],{"class":135},[33,646,647,649,652,654,656,658,660,663],{"class":35,"line":216},[33,648,515],{"class":171},[33,650,651],{"class":201}," action",[33,653,521],{"class":171},[33,655,524],{"class":135},[33,657,527],{"class":139},[33,659,143],{"class":135},[33,661,662],{"class":146},"\"[data-action]\"",[33,664,665],{"class":135},");\n",[33,667,668,670,672,674,677,679],{"class":35,"line":245},[33,669,541],{"class":171},[33,671,544],{"class":135},[33,673,547],{"class":171},[33,675,676],{"class":135},"action) ",[33,678,553],{"class":171},[33,680,681],{"class":135},";\n",[33,683,684],{"class":35,"line":259},[33,685,123],{"emptyLinePlaceholder":122},[33,687,688,691],{"class":35,"line":272},[33,689,690],{"class":171},"    switch",[33,692,693],{"class":135}," (action.dataset.action) {\n",[33,695,696,699,702],{"class":35,"line":277},[33,697,698],{"class":171},"        case",[33,700,701],{"class":146}," \"delete\"",[33,703,704],{"class":135},":\n",[33,706,707,710,713,715],{"class":35,"line":283},[33,708,709],{"class":171},"            const",[33,711,712],{"class":201}," id",[33,714,521],{"class":171},[33,716,717],{"class":135}," action.dataset.id;\n",[33,719,720,723],{"class":35,"line":289},[33,721,722],{"class":139},"            deleteItem",[33,724,725],{"class":135},"(id);\n",[33,727,728,731],{"class":35,"line":295},[33,729,730],{"class":171},"            break",[33,732,681],{"class":135},[33,734,735,737,740],{"class":35,"line":301},[33,736,698],{"class":171},[33,738,739],{"class":146}," \"edit\"",[33,741,704],{"class":135},[33,743,744,747],{"class":35,"line":306},[33,745,746],{"class":139},"            editItem",[33,748,749],{"class":135},"(action.dataset.id);\n",[33,751,752,754],{"class":35,"line":312},[33,753,730],{"class":171},[33,755,681],{"class":135},[33,757,758,760,763],{"class":35,"line":341},[33,759,698],{"class":171},[33,761,762],{"class":146}," \"save\"",[33,764,704],{"class":135},[33,766,767,770,773,775,777,780],{"class":35,"line":354},[33,768,769],{"class":139},"            saveForm",[33,771,772],{"class":135},"(action.",[33,774,527],{"class":139},[33,776,143],{"class":135},[33,778,779],{"class":146},"\"form\"",[33,781,782],{"class":135},"));\n",[33,784,785,787],{"class":35,"line":367},[33,786,730],{"class":171},[33,788,681],{"class":135},[33,790,791],{"class":35,"line":373},[33,792,793],{"class":135},"    }\n",[33,795,796],{"class":35,"line":379},[33,797,382],{"class":135},[15,799,801,804,805],{"id":800},"stoppropagation-vs-stopimmediatepropagation",[30,802,803],{},"stopPropagation"," vs ",[30,806,807],{},"stopImmediatePropagation",[20,809,810],{"language":22},[24,811,813],{"className":26,"code":812,"language":22,"meta":28,"style":28},"\u002F\u002F ── stopPropagation: stop the event from propagating (bubbling\u002Fcapturing further) ──\n\u002F\u002F But other listeners ON THE SAME ELEMENT still fire.\ndocument.querySelector(\"#button\").addEventListener(\"click\", (e) => {\n    e.stopPropagation();  \u002F\u002F parent listeners won't fire\n    console.log(\"listener 1\");  \u002F\u002F fires\n});\ndocument.querySelector(\"#button\").addEventListener(\"click\", (e) => {\n    console.log(\"listener 2\");  \u002F\u002F ALSO fires (same element, different listener)\n});\n\n\u002F\u002F ── stopImmediatePropagation: stop propagation AND prevent other listeners on same element ──\ndocument.querySelector(\"#button\").addEventListener(\"click\", (e) => {\n    e.stopImmediatePropagation();  \u002F\u002F no further listeners fire (same element + propagation)\n    console.log(\"listener 1\");  \u002F\u002F fires\n});\ndocument.querySelector(\"#button\").addEventListener(\"click\", (e) => {\n    console.log(\"listener 2\");  \u002F\u002F DOES NOT fire (stopImmediatePropagation blocked it)\n});\n\n\u002F\u002F ── preventDefault: prevent the default browser behavior ──\ndocument.querySelector(\"form\").addEventListener(\"submit\", (e) => {\n    e.preventDefault();  \u002F\u002F prevents form submission (page reload)\n    \u002F\u002F handle the form with JS instead\n});\ndocument.querySelector(\"a\").addEventListener(\"click\", (e) => {\n    e.preventDefault();  \u002F\u002F prevents navigation\n    \u002F\u002F handle with client-side routing instead\n});\n\u002F\u002F ⚠️ preventDefault doesn't stop propagation — the event still bubbles.\n\u002F\u002F To stop both: e.preventDefault(); e.stopPropagation();\n",[30,814,815,820,825,854,867,883,887,915,931,935,939,944,972,983,997,1001,1029,1044,1048,1052,1057,1086,1098,1103,1107,1136,1147,1152,1156,1161],{"__ignoreMap":28},[33,816,817],{"class":35,"line":36},[33,818,819],{"class":39},"\u002F\u002F ── stopPropagation: stop the event from propagating (bubbling\u002Fcapturing further) ──\n",[33,821,822],{"class":35,"line":43},[33,823,824],{"class":39},"\u002F\u002F But other listeners ON THE SAME ELEMENT still fire.\n",[33,826,827,829,831,833,836,838,840,842,844,846,848,850,852],{"class":35,"line":49},[33,828,136],{"class":135},[33,830,140],{"class":139},[33,832,143],{"class":135},[33,834,835],{"class":146},"\"#button\"",[33,837,150],{"class":135},[33,839,153],{"class":139},[33,841,143],{"class":135},[33,843,158],{"class":146},[33,845,161],{"class":135},[33,847,165],{"class":164},[33,849,168],{"class":135},[33,851,172],{"class":171},[33,853,175],{"class":135},[33,855,856,859,861,864],{"class":35,"line":55},[33,857,858],{"class":135},"    e.",[33,860,803],{"class":139},[33,862,863],{"class":135},"();  ",[33,865,866],{"class":39},"\u002F\u002F parent listeners won't fire\n",[33,868,869,871,873,875,878,880],{"class":35,"line":61},[33,870,181],{"class":135},[33,872,184],{"class":139},[33,874,143],{"class":135},[33,876,877],{"class":146},"\"listener 1\"",[33,879,205],{"class":135},[33,881,882],{"class":39},"\u002F\u002F fires\n",[33,884,885],{"class":35,"line":67},[33,886,382],{"class":135},[33,888,889,891,893,895,897,899,901,903,905,907,909,911,913],{"class":35,"line":72},[33,890,136],{"class":135},[33,892,140],{"class":139},[33,894,143],{"class":135},[33,896,835],{"class":146},[33,898,150],{"class":135},[33,900,153],{"class":139},[33,902,143],{"class":135},[33,904,158],{"class":146},[33,906,161],{"class":135},[33,908,165],{"class":164},[33,910,168],{"class":135},[33,912,172],{"class":171},[33,914,175],{"class":135},[33,916,917,919,921,923,926,928],{"class":35,"line":78},[33,918,181],{"class":135},[33,920,184],{"class":139},[33,922,143],{"class":135},[33,924,925],{"class":146},"\"listener 2\"",[33,927,205],{"class":135},[33,929,930],{"class":39},"\u002F\u002F ALSO fires (same element, different listener)\n",[33,932,933],{"class":35,"line":84},[33,934,382],{"class":135},[33,936,937],{"class":35,"line":90},[33,938,123],{"emptyLinePlaceholder":122},[33,940,941],{"class":35,"line":96},[33,942,943],{"class":39},"\u002F\u002F ── stopImmediatePropagation: stop propagation AND prevent other listeners on same element ──\n",[33,945,946,948,950,952,954,956,958,960,962,964,966,968,970],{"class":35,"line":101},[33,947,136],{"class":135},[33,949,140],{"class":139},[33,951,143],{"class":135},[33,953,835],{"class":146},[33,955,150],{"class":135},[33,957,153],{"class":139},[33,959,143],{"class":135},[33,961,158],{"class":146},[33,963,161],{"class":135},[33,965,165],{"class":164},[33,967,168],{"class":135},[33,969,172],{"class":171},[33,971,175],{"class":135},[33,973,974,976,978,980],{"class":35,"line":107},[33,975,858],{"class":135},[33,977,807],{"class":139},[33,979,863],{"class":135},[33,981,982],{"class":39},"\u002F\u002F no further listeners fire (same element + propagation)\n",[33,984,985,987,989,991,993,995],{"class":35,"line":113},[33,986,181],{"class":135},[33,988,184],{"class":139},[33,990,143],{"class":135},[33,992,877],{"class":146},[33,994,205],{"class":135},[33,996,882],{"class":39},[33,998,999],{"class":35,"line":119},[33,1000,382],{"class":135},[33,1002,1003,1005,1007,1009,1011,1013,1015,1017,1019,1021,1023,1025,1027],{"class":35,"line":126},[33,1004,136],{"class":135},[33,1006,140],{"class":139},[33,1008,143],{"class":135},[33,1010,835],{"class":146},[33,1012,150],{"class":135},[33,1014,153],{"class":139},[33,1016,143],{"class":135},[33,1018,158],{"class":146},[33,1020,161],{"class":135},[33,1022,165],{"class":164},[33,1024,168],{"class":135},[33,1026,172],{"class":171},[33,1028,175],{"class":135},[33,1030,1031,1033,1035,1037,1039,1041],{"class":35,"line":132},[33,1032,181],{"class":135},[33,1034,184],{"class":139},[33,1036,143],{"class":135},[33,1038,925],{"class":146},[33,1040,205],{"class":135},[33,1042,1043],{"class":39},"\u002F\u002F DOES NOT fire (stopImmediatePropagation blocked it)\n",[33,1045,1046],{"class":35,"line":178},[33,1047,382],{"class":135},[33,1049,1050],{"class":35,"line":195},[33,1051,123],{"emptyLinePlaceholder":122},[33,1053,1054],{"class":35,"line":211},[33,1055,1056],{"class":39},"\u002F\u002F ── preventDefault: prevent the default browser behavior ──\n",[33,1058,1059,1061,1063,1065,1067,1069,1071,1073,1076,1078,1080,1082,1084],{"class":35,"line":216},[33,1060,136],{"class":135},[33,1062,140],{"class":139},[33,1064,143],{"class":135},[33,1066,779],{"class":146},[33,1068,150],{"class":135},[33,1070,153],{"class":139},[33,1072,143],{"class":135},[33,1074,1075],{"class":146},"\"submit\"",[33,1077,161],{"class":135},[33,1079,165],{"class":164},[33,1081,168],{"class":135},[33,1083,172],{"class":171},[33,1085,175],{"class":135},[33,1087,1088,1090,1093,1095],{"class":35,"line":245},[33,1089,858],{"class":135},[33,1091,1092],{"class":139},"preventDefault",[33,1094,863],{"class":135},[33,1096,1097],{"class":39},"\u002F\u002F prevents form submission (page reload)\n",[33,1099,1100],{"class":35,"line":259},[33,1101,1102],{"class":39},"    \u002F\u002F handle the form with JS instead\n",[33,1104,1105],{"class":35,"line":272},[33,1106,382],{"class":135},[33,1108,1109,1111,1113,1115,1118,1120,1122,1124,1126,1128,1130,1132,1134],{"class":35,"line":277},[33,1110,136],{"class":135},[33,1112,140],{"class":139},[33,1114,143],{"class":135},[33,1116,1117],{"class":146},"\"a\"",[33,1119,150],{"class":135},[33,1121,153],{"class":139},[33,1123,143],{"class":135},[33,1125,158],{"class":146},[33,1127,161],{"class":135},[33,1129,165],{"class":164},[33,1131,168],{"class":135},[33,1133,172],{"class":171},[33,1135,175],{"class":135},[33,1137,1138,1140,1142,1144],{"class":35,"line":283},[33,1139,858],{"class":135},[33,1141,1092],{"class":139},[33,1143,863],{"class":135},[33,1145,1146],{"class":39},"\u002F\u002F prevents navigation\n",[33,1148,1149],{"class":35,"line":289},[33,1150,1151],{"class":39},"    \u002F\u002F handle with client-side routing instead\n",[33,1153,1154],{"class":35,"line":295},[33,1155,382],{"class":135},[33,1157,1158],{"class":35,"line":301},[33,1159,1160],{"class":39},"\u002F\u002F ⚠️ preventDefault doesn't stop propagation — the event still bubbles.\n",[33,1162,1163],{"class":35,"line":306},[33,1164,1165],{"class":39},"\u002F\u002F To stop both: e.preventDefault(); e.stopPropagation();\n",[15,1167,1169],{"id":1168},"custom-events","Custom Events",[20,1171,1172],{"language":22},[24,1173,1175],{"className":26,"code":1174,"language":22,"meta":28,"style":28},"\u002F\u002F ── CustomEvent: create and dispatch custom events ──\nconst target = document.querySelector(\"#app\");\n\n\u002F\u002F Listen for a custom event\ntarget.addEventListener(\"app:ready\", (e) => {\n    console.log(\"app is ready\", e.detail);  \u002F\u002F detail: custom data\n});\n\n\u002F\u002F Create and dispatch a custom event\nconst event = new CustomEvent(\"app:ready\", {\n    detail: { version: \"1.0\", timestamp: Date.now() },  \u002F\u002F custom data (accessible via e.detail)\n    bubbles: true,       \u002F\u002F event bubbles (default: false)\n    cancelable: true,    \u002F\u002F event can be preventDefault'd (default: false)\n    composed: true,      \u002F\u002F event crosses Shadow DOM boundaries (default: false)\n});\ntarget.dispatchEvent(event);\n\n\u002F\u002F ── Use case: component communication ──\nclass Store extends EventTarget {\n    #state = {};\n    set(key, value) {\n        this.#state[key] = value;\n        this.dispatchEvent(new CustomEvent(\"change\", {\n            detail: { key, value, state: { ...this.#state } },\n        }));\n    }\n}\nconst store = new Store();\nstore.addEventListener(\"change\", (e) => {\n    console.log(\"state changed:\", e.detail.key, e.detail.value);\n});\nstore.set(\"user\", { name: \"Alice\" });  \u002F\u002F dispatches \"change\" event\n\n\u002F\u002F ── AbortController: cancel event listeners (modern cleanup) ──\nconst controller = new AbortController();\ndocument.addEventListener(\"scroll\", handler, { signal: controller.signal });\n\u002F\u002F Later: cancel the listener (and any other listeners with the same signal)\ncontroller.abort();  \u002F\u002F removes the scroll listener (no need for removeEventListener)\n",[30,1176,1177,1182,1203,1207,1212,1234,1251,1255,1259,1264,1286,1306,1319,1332,1345,1349,1359,1363,1368,1384,1394,1413,1427,1450,1464,1469,1473,1478,1494,1515,1529,1533,1557,1561,1566,1582,1596,1602],{"__ignoreMap":28},[33,1178,1179],{"class":35,"line":36},[33,1180,1181],{"class":39},"\u002F\u002F ── CustomEvent: create and dispatch custom events ──\n",[33,1183,1184,1187,1190,1192,1195,1197,1199,1201],{"class":35,"line":43},[33,1185,1186],{"class":171},"const",[33,1188,1189],{"class":201}," target",[33,1191,521],{"class":171},[33,1193,1194],{"class":135}," document.",[33,1196,140],{"class":139},[33,1198,143],{"class":135},[33,1200,626],{"class":146},[33,1202,665],{"class":135},[33,1204,1205],{"class":35,"line":49},[33,1206,123],{"emptyLinePlaceholder":122},[33,1208,1209],{"class":35,"line":55},[33,1210,1211],{"class":39},"\u002F\u002F Listen for a custom event\n",[33,1213,1214,1217,1219,1221,1224,1226,1228,1230,1232],{"class":35,"line":61},[33,1215,1216],{"class":135},"target.",[33,1218,153],{"class":139},[33,1220,143],{"class":135},[33,1222,1223],{"class":146},"\"app:ready\"",[33,1225,161],{"class":135},[33,1227,165],{"class":164},[33,1229,168],{"class":135},[33,1231,172],{"class":171},[33,1233,175],{"class":135},[33,1235,1236,1238,1240,1242,1245,1248],{"class":35,"line":67},[33,1237,181],{"class":135},[33,1239,184],{"class":139},[33,1241,143],{"class":135},[33,1243,1244],{"class":146},"\"app is ready\"",[33,1246,1247],{"class":135},", e.detail);  ",[33,1249,1250],{"class":39},"\u002F\u002F detail: custom data\n",[33,1252,1253],{"class":35,"line":72},[33,1254,382],{"class":135},[33,1256,1257],{"class":35,"line":78},[33,1258,123],{"emptyLinePlaceholder":122},[33,1260,1261],{"class":35,"line":84},[33,1262,1263],{"class":39},"\u002F\u002F Create and dispatch a custom event\n",[33,1265,1266,1268,1271,1273,1276,1279,1281,1283],{"class":35,"line":90},[33,1267,1186],{"class":171},[33,1269,1270],{"class":201}," event",[33,1272,521],{"class":171},[33,1274,1275],{"class":171}," new",[33,1277,1278],{"class":139}," CustomEvent",[33,1280,143],{"class":135},[33,1282,1223],{"class":146},[33,1284,1285],{"class":135},", {\n",[33,1287,1288,1291,1294,1297,1300,1303],{"class":35,"line":96},[33,1289,1290],{"class":135},"    detail: { version: ",[33,1292,1293],{"class":146},"\"1.0\"",[33,1295,1296],{"class":135},", timestamp: Date.",[33,1298,1299],{"class":139},"now",[33,1301,1302],{"class":135},"() },  ",[33,1304,1305],{"class":39},"\u002F\u002F custom data (accessible via e.detail)\n",[33,1307,1308,1311,1313,1316],{"class":35,"line":101},[33,1309,1310],{"class":135},"    bubbles: ",[33,1312,264],{"class":201},[33,1314,1315],{"class":135},",       ",[33,1317,1318],{"class":39},"\u002F\u002F event bubbles (default: false)\n",[33,1320,1321,1324,1326,1329],{"class":35,"line":107},[33,1322,1323],{"class":135},"    cancelable: ",[33,1325,264],{"class":201},[33,1327,1328],{"class":135},",    ",[33,1330,1331],{"class":39},"\u002F\u002F event can be preventDefault'd (default: false)\n",[33,1333,1334,1337,1339,1342],{"class":35,"line":113},[33,1335,1336],{"class":135},"    composed: ",[33,1338,264],{"class":201},[33,1340,1341],{"class":135},",      ",[33,1343,1344],{"class":39},"\u002F\u002F event crosses Shadow DOM boundaries (default: false)\n",[33,1346,1347],{"class":35,"line":119},[33,1348,382],{"class":135},[33,1350,1351,1353,1356],{"class":35,"line":126},[33,1352,1216],{"class":135},[33,1354,1355],{"class":139},"dispatchEvent",[33,1357,1358],{"class":135},"(event);\n",[33,1360,1361],{"class":35,"line":132},[33,1362,123],{"emptyLinePlaceholder":122},[33,1364,1365],{"class":35,"line":178},[33,1366,1367],{"class":39},"\u002F\u002F ── Use case: component communication ──\n",[33,1369,1370,1373,1376,1379,1382],{"class":35,"line":195},[33,1371,1372],{"class":171},"class",[33,1374,1375],{"class":139}," Store",[33,1377,1378],{"class":171}," extends",[33,1380,1381],{"class":139}," EventTarget",[33,1383,175],{"class":135},[33,1385,1386,1389,1391],{"class":35,"line":211},[33,1387,1388],{"class":164},"    #state",[33,1390,521],{"class":171},[33,1392,1393],{"class":135}," {};\n",[33,1395,1396,1399,1401,1404,1407,1410],{"class":35,"line":216},[33,1397,1398],{"class":139},"    set",[33,1400,143],{"class":135},[33,1402,1403],{"class":164},"key",[33,1405,1406],{"class":135},", ",[33,1408,1409],{"class":164},"value",[33,1411,1412],{"class":135},") {\n",[33,1414,1415,1418,1421,1424],{"class":35,"line":245},[33,1416,1417],{"class":201},"        this",[33,1419,1420],{"class":135},".#state[key] ",[33,1422,1423],{"class":171},"=",[33,1425,1426],{"class":135}," value;\n",[33,1428,1429,1431,1434,1436,1438,1441,1443,1445,1448],{"class":35,"line":259},[33,1430,1417],{"class":201},[33,1432,1433],{"class":135},".",[33,1435,1355],{"class":139},[33,1437,143],{"class":135},[33,1439,1440],{"class":171},"new",[33,1442,1278],{"class":139},[33,1444,143],{"class":135},[33,1446,1447],{"class":146},"\"change\"",[33,1449,1285],{"class":135},[33,1451,1452,1455,1458,1461],{"class":35,"line":272},[33,1453,1454],{"class":135},"            detail: { key, value, state: { ",[33,1456,1457],{"class":171},"...",[33,1459,1460],{"class":201},"this",[33,1462,1463],{"class":135},".#state } },\n",[33,1465,1466],{"class":35,"line":277},[33,1467,1468],{"class":135},"        }));\n",[33,1470,1471],{"class":35,"line":283},[33,1472,793],{"class":135},[33,1474,1475],{"class":35,"line":289},[33,1476,1477],{"class":135},"}\n",[33,1479,1480,1482,1485,1487,1489,1491],{"class":35,"line":295},[33,1481,1186],{"class":171},[33,1483,1484],{"class":201}," store",[33,1486,521],{"class":171},[33,1488,1275],{"class":171},[33,1490,1375],{"class":139},[33,1492,1493],{"class":135},"();\n",[33,1495,1496,1499,1501,1503,1505,1507,1509,1511,1513],{"class":35,"line":301},[33,1497,1498],{"class":135},"store.",[33,1500,153],{"class":139},[33,1502,143],{"class":135},[33,1504,1447],{"class":146},[33,1506,161],{"class":135},[33,1508,165],{"class":164},[33,1510,168],{"class":135},[33,1512,172],{"class":171},[33,1514,175],{"class":135},[33,1516,1517,1519,1521,1523,1526],{"class":35,"line":306},[33,1518,181],{"class":135},[33,1520,184],{"class":139},[33,1522,143],{"class":135},[33,1524,1525],{"class":146},"\"state changed:\"",[33,1527,1528],{"class":135},", e.detail.key, e.detail.value);\n",[33,1530,1531],{"class":35,"line":312},[33,1532,382],{"class":135},[33,1534,1535,1537,1540,1542,1545,1548,1551,1554],{"class":35,"line":341},[33,1536,1498],{"class":135},[33,1538,1539],{"class":139},"set",[33,1541,143],{"class":135},[33,1543,1544],{"class":146},"\"user\"",[33,1546,1547],{"class":135},", { name: ",[33,1549,1550],{"class":146},"\"Alice\"",[33,1552,1553],{"class":135}," });  ",[33,1555,1556],{"class":39},"\u002F\u002F dispatches \"change\" event\n",[33,1558,1559],{"class":35,"line":354},[33,1560,123],{"emptyLinePlaceholder":122},[33,1562,1563],{"class":35,"line":367},[33,1564,1565],{"class":39},"\u002F\u002F ── AbortController: cancel event listeners (modern cleanup) ──\n",[33,1567,1568,1570,1573,1575,1577,1580],{"class":35,"line":373},[33,1569,1186],{"class":171},[33,1571,1572],{"class":201}," controller",[33,1574,521],{"class":171},[33,1576,1275],{"class":171},[33,1578,1579],{"class":139}," AbortController",[33,1581,1493],{"class":135},[33,1583,1584,1586,1588,1590,1593],{"class":35,"line":379},[33,1585,136],{"class":135},[33,1587,153],{"class":139},[33,1589,143],{"class":135},[33,1591,1592],{"class":146},"\"scroll\"",[33,1594,1595],{"class":135},", handler, { signal: controller.signal });\n",[33,1597,1599],{"class":35,"line":1598},37,[33,1600,1601],{"class":39},"\u002F\u002F Later: cancel the listener (and any other listeners with the same signal)\n",[33,1603,1605,1608,1611,1613],{"class":35,"line":1604},38,[33,1606,1607],{"class":135},"controller.",[33,1609,1610],{"class":139},"abort",[33,1612,863],{"class":135},[33,1614,1615],{"class":39},"\u002F\u002F removes the scroll listener (no need for removeEventListener)\n",[15,1617,1619],{"id":1618},"passive-listeners-for-scroll-performance","Passive Listeners for Scroll Performance",[20,1621,1622],{"language":22},[24,1623,1625],{"className":26,"code":1624,"language":22,"meta":28,"style":28},"\u002F\u002F ── Passive listeners: tell the browser the handler won't call preventDefault ──\n\u002F\u002F This allows the browser to scroll immediately (without waiting for the handler to finish).\n\u002F\u002F Critical for touch\u002Fwheel events — non-passive listeners can cause scroll jank.\n\n\u002F\u002F ❌ NON-PASSIVE (default): browser waits for the handler before scrolling (can cause jank)\ndocument.addEventListener(\"touchmove\", (e) => {\n    \u002F\u002F if this calls e.preventDefault(), scrolling is blocked\n    \u002F\u002F the browser must WAIT for this handler to finish before scrolling\n    updateIndicator();\n}, { passive: false });  \u002F\u002F default — browser must wait\n\n\u002F\u002F ✅ PASSIVE: browser scrolls immediately (handler runs but can't block)\ndocument.addEventListener(\"touchmove\", (e) => {\n    updateIndicator();  \u002F\u002F runs, but e.preventDefault() does NOTHING (passive ignores it)\n}, { passive: true });  \u002F\u002F browser doesn't wait — scrolls immediately\n\u002F\u002F Use passive for scroll\u002Ftouch\u002Fwheel handlers that don't need preventDefault.\n\n\u002F\u002F ── once: listener auto-removes after first invocation ──\ndocument.addEventListener(\"DOMContentLoaded\", init, { once: true });\n\u002F\u002F equivalent to: document.addEventListener(\"DOMContentLoaded\", () => {\n\u002F\u002F     init();\n\u002F\u002F     document.removeEventListener(\"DOMContentLoaded\", init);\n\u002F\u002F });\n\n\u002F\u002F ── Combining options ──\ndocument.addEventListener(\"scroll\", handleScroll, {\n    passive: true,   \u002F\u002F won't call preventDefault (fast scroll)\n    signal: controller.signal,  \u002F\u002F can be aborted (clean removal)\n});\n",[30,1626,1627,1632,1637,1642,1646,1651,1672,1677,1682,1689,1701,1705,1710,1730,1739,1750,1755,1759,1764,1783,1788,1793,1798,1803,1807,1812,1825,1838,1846],{"__ignoreMap":28},[33,1628,1629],{"class":35,"line":36},[33,1630,1631],{"class":39},"\u002F\u002F ── Passive listeners: tell the browser the handler won't call preventDefault ──\n",[33,1633,1634],{"class":35,"line":43},[33,1635,1636],{"class":39},"\u002F\u002F This allows the browser to scroll immediately (without waiting for the handler to finish).\n",[33,1638,1639],{"class":35,"line":49},[33,1640,1641],{"class":39},"\u002F\u002F Critical for touch\u002Fwheel events — non-passive listeners can cause scroll jank.\n",[33,1643,1644],{"class":35,"line":55},[33,1645,123],{"emptyLinePlaceholder":122},[33,1647,1648],{"class":35,"line":61},[33,1649,1650],{"class":39},"\u002F\u002F ❌ NON-PASSIVE (default): browser waits for the handler before scrolling (can cause jank)\n",[33,1652,1653,1655,1657,1659,1662,1664,1666,1668,1670],{"class":35,"line":67},[33,1654,136],{"class":135},[33,1656,153],{"class":139},[33,1658,143],{"class":135},[33,1660,1661],{"class":146},"\"touchmove\"",[33,1663,161],{"class":135},[33,1665,165],{"class":164},[33,1667,168],{"class":135},[33,1669,172],{"class":171},[33,1671,175],{"class":135},[33,1673,1674],{"class":35,"line":72},[33,1675,1676],{"class":39},"    \u002F\u002F if this calls e.preventDefault(), scrolling is blocked\n",[33,1678,1679],{"class":35,"line":78},[33,1680,1681],{"class":39},"    \u002F\u002F the browser must WAIT for this handler to finish before scrolling\n",[33,1683,1684,1687],{"class":35,"line":84},[33,1685,1686],{"class":139},"    updateIndicator",[33,1688,1493],{"class":135},[33,1690,1691,1694,1696,1698],{"class":35,"line":90},[33,1692,1693],{"class":135},"}, { passive: ",[33,1695,202],{"class":201},[33,1697,1553],{"class":135},[33,1699,1700],{"class":39},"\u002F\u002F default — browser must wait\n",[33,1702,1703],{"class":35,"line":96},[33,1704,123],{"emptyLinePlaceholder":122},[33,1706,1707],{"class":35,"line":101},[33,1708,1709],{"class":39},"\u002F\u002F ✅ PASSIVE: browser scrolls immediately (handler runs but can't block)\n",[33,1711,1712,1714,1716,1718,1720,1722,1724,1726,1728],{"class":35,"line":107},[33,1713,136],{"class":135},[33,1715,153],{"class":139},[33,1717,143],{"class":135},[33,1719,1661],{"class":146},[33,1721,161],{"class":135},[33,1723,165],{"class":164},[33,1725,168],{"class":135},[33,1727,172],{"class":171},[33,1729,175],{"class":135},[33,1731,1732,1734,1736],{"class":35,"line":113},[33,1733,1686],{"class":139},[33,1735,863],{"class":135},[33,1737,1738],{"class":39},"\u002F\u002F runs, but e.preventDefault() does NOTHING (passive ignores it)\n",[33,1740,1741,1743,1745,1747],{"class":35,"line":119},[33,1742,1693],{"class":135},[33,1744,264],{"class":201},[33,1746,1553],{"class":135},[33,1748,1749],{"class":39},"\u002F\u002F browser doesn't wait — scrolls immediately\n",[33,1751,1752],{"class":35,"line":126},[33,1753,1754],{"class":39},"\u002F\u002F Use passive for scroll\u002Ftouch\u002Fwheel handlers that don't need preventDefault.\n",[33,1756,1757],{"class":35,"line":132},[33,1758,123],{"emptyLinePlaceholder":122},[33,1760,1761],{"class":35,"line":178},[33,1762,1763],{"class":39},"\u002F\u002F ── once: listener auto-removes after first invocation ──\n",[33,1765,1766,1768,1770,1772,1775,1778,1780],{"class":35,"line":195},[33,1767,136],{"class":135},[33,1769,153],{"class":139},[33,1771,143],{"class":135},[33,1773,1774],{"class":146},"\"DOMContentLoaded\"",[33,1776,1777],{"class":135},", init, { once: ",[33,1779,264],{"class":201},[33,1781,1782],{"class":135}," });\n",[33,1784,1785],{"class":35,"line":211},[33,1786,1787],{"class":39},"\u002F\u002F equivalent to: document.addEventListener(\"DOMContentLoaded\", () => {\n",[33,1789,1790],{"class":35,"line":216},[33,1791,1792],{"class":39},"\u002F\u002F     init();\n",[33,1794,1795],{"class":35,"line":245},[33,1796,1797],{"class":39},"\u002F\u002F     document.removeEventListener(\"DOMContentLoaded\", init);\n",[33,1799,1800],{"class":35,"line":259},[33,1801,1802],{"class":39},"\u002F\u002F });\n",[33,1804,1805],{"class":35,"line":272},[33,1806,123],{"emptyLinePlaceholder":122},[33,1808,1809],{"class":35,"line":277},[33,1810,1811],{"class":39},"\u002F\u002F ── Combining options ──\n",[33,1813,1814,1816,1818,1820,1822],{"class":35,"line":283},[33,1815,136],{"class":135},[33,1817,153],{"class":139},[33,1819,143],{"class":135},[33,1821,1592],{"class":146},[33,1823,1824],{"class":135},", handleScroll, {\n",[33,1826,1827,1830,1832,1835],{"class":35,"line":289},[33,1828,1829],{"class":135},"    passive: ",[33,1831,264],{"class":201},[33,1833,1834],{"class":135},",   ",[33,1836,1837],{"class":39},"\u002F\u002F won't call preventDefault (fast scroll)\n",[33,1839,1840,1843],{"class":35,"line":295},[33,1841,1842],{"class":135},"    signal: controller.signal,  ",[33,1844,1845],{"class":39},"\u002F\u002F can be aborted (clean removal)\n",[33,1847,1848],{"class":35,"line":301},[33,1849,382],{"class":135},[15,1851,1853],{"id":1852},"tips-tricks","💡 Tips & Tricks",[20,1855,1856],{"language":22},[24,1857,1859],{"className":26,"code":1858,"language":22,"meta":28,"style":28},"\u002F\u002F ── Event delegation with data attributes ──\nlist.addEventListener(\"click\", (e) => {\n    const action = e.target.closest(\"[data-action]\");\n    if (action) handleAction(action.dataset.action, action.dataset.id);\n});\n\n\u002F\u002F ── Debounced scroll\u002Fresize listeners (avoid excessive calls) ──\nconst debounce = (fn, ms) => {\n    let timer;\n    return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), ms); };\n};\nwindow.addEventListener(\"scroll\", debounce(() => {\n    console.log(\"scroll position:\", window.scrollY);\n}, 100));\n\n\u002F\u002F ── AbortController for bulk listener cleanup ──\nfunction setupListeners(element) {\n    const controller = new AbortController();\n    element.addEventListener(\"click\", handler1, { signal: controller.signal });\n    element.addEventListener(\"input\", handler2, { signal: controller.signal });\n    element.addEventListener(\"keydown\", handler3, { signal: controller.signal });\n    return () => controller.abort();  \u002F\u002F one call removes ALL three listeners\n}\nconst cleanup = setupListeners(form);\n\u002F\u002F Later: cleanup();  \u002F\u002F removes all listeners at once (no individual removeEventListener calls)\n\n\u002F\u002F ── Check if default was prevented ──\nelement.addEventListener(\"click\", (e) => {\n    if (!e.defaultPrevented) {  \u002F\u002F another handler hasn't called preventDefault\n        doSomething();\n    }\n});\n\n\u002F\u002F ── Dispatch synthetic events (for testing or automation) ──\nconst clickEvent = new MouseEvent(\"click\", { bubbles: true, cancelable: true });\nelement.dispatchEvent(clickEvent);  \u002F\u002F triggers click listeners as if the user clicked\n",[30,1860,1861,1866,1887,1905,1918,1922,1926,1931,1956,1964,2009,2014,2036,2050,2059,2063,2068,2083,2097,2111,2125,2139,2158,2162,2176,2184,2188,2193,2214,2228,2235,2239,2243,2247,2252,2282],{"__ignoreMap":28},[33,1862,1863],{"class":35,"line":36},[33,1864,1865],{"class":39},"\u002F\u002F ── Event delegation with data attributes ──\n",[33,1867,1868,1871,1873,1875,1877,1879,1881,1883,1885],{"class":35,"line":43},[33,1869,1870],{"class":135},"list.",[33,1872,153],{"class":139},[33,1874,143],{"class":135},[33,1876,158],{"class":146},[33,1878,161],{"class":135},[33,1880,165],{"class":164},[33,1882,168],{"class":135},[33,1884,172],{"class":171},[33,1886,175],{"class":135},[33,1888,1889,1891,1893,1895,1897,1899,1901,1903],{"class":35,"line":49},[33,1890,515],{"class":171},[33,1892,651],{"class":201},[33,1894,521],{"class":171},[33,1896,524],{"class":135},[33,1898,527],{"class":139},[33,1900,143],{"class":135},[33,1902,662],{"class":146},[33,1904,665],{"class":135},[33,1906,1907,1909,1912,1915],{"class":35,"line":55},[33,1908,541],{"class":171},[33,1910,1911],{"class":135}," (action) ",[33,1913,1914],{"class":139},"handleAction",[33,1916,1917],{"class":135},"(action.dataset.action, action.dataset.id);\n",[33,1919,1920],{"class":35,"line":61},[33,1921,382],{"class":135},[33,1923,1924],{"class":35,"line":67},[33,1925,123],{"emptyLinePlaceholder":122},[33,1927,1928],{"class":35,"line":72},[33,1929,1930],{"class":39},"\u002F\u002F ── Debounced scroll\u002Fresize listeners (avoid excessive calls) ──\n",[33,1932,1933,1935,1938,1940,1942,1945,1947,1950,1952,1954],{"class":35,"line":78},[33,1934,1186],{"class":171},[33,1936,1937],{"class":139}," debounce",[33,1939,521],{"class":171},[33,1941,544],{"class":135},[33,1943,1944],{"class":164},"fn",[33,1946,1406],{"class":135},[33,1948,1949],{"class":164},"ms",[33,1951,168],{"class":135},[33,1953,172],{"class":171},[33,1955,175],{"class":135},[33,1957,1958,1961],{"class":35,"line":84},[33,1959,1960],{"class":171},"    let",[33,1962,1963],{"class":135}," timer;\n",[33,1965,1966,1969,1971,1973,1976,1978,1980,1983,1986,1989,1991,1994,1997,1999,2002,2004,2006],{"class":35,"line":90},[33,1967,1968],{"class":171},"    return",[33,1970,544],{"class":135},[33,1972,1457],{"class":171},[33,1974,1975],{"class":164},"args",[33,1977,168],{"class":135},[33,1979,172],{"class":171},[33,1981,1982],{"class":135}," { ",[33,1984,1985],{"class":139},"clearTimeout",[33,1987,1988],{"class":135},"(timer); timer ",[33,1990,1423],{"class":171},[33,1992,1993],{"class":139}," setTimeout",[33,1995,1996],{"class":135},"(() ",[33,1998,172],{"class":171},[33,2000,2001],{"class":139}," fn",[33,2003,143],{"class":135},[33,2005,1457],{"class":171},[33,2007,2008],{"class":135},"args), ms); };\n",[33,2010,2011],{"class":35,"line":96},[33,2012,2013],{"class":135},"};\n",[33,2015,2016,2019,2021,2023,2025,2027,2030,2032,2034],{"class":35,"line":101},[33,2017,2018],{"class":135},"window.",[33,2020,153],{"class":139},[33,2022,143],{"class":135},[33,2024,1592],{"class":146},[33,2026,1406],{"class":135},[33,2028,2029],{"class":139},"debounce",[33,2031,1996],{"class":135},[33,2033,172],{"class":171},[33,2035,175],{"class":135},[33,2037,2038,2040,2042,2044,2047],{"class":35,"line":107},[33,2039,181],{"class":135},[33,2041,184],{"class":139},[33,2043,143],{"class":135},[33,2045,2046],{"class":146},"\"scroll position:\"",[33,2048,2049],{"class":135},", window.scrollY);\n",[33,2051,2052,2054,2057],{"class":35,"line":113},[33,2053,198],{"class":135},[33,2055,2056],{"class":201},"100",[33,2058,782],{"class":135},[33,2060,2061],{"class":35,"line":119},[33,2062,123],{"emptyLinePlaceholder":122},[33,2064,2065],{"class":35,"line":126},[33,2066,2067],{"class":39},"\u002F\u002F ── AbortController for bulk listener cleanup ──\n",[33,2069,2070,2073,2076,2078,2081],{"class":35,"line":132},[33,2071,2072],{"class":171},"function",[33,2074,2075],{"class":139}," setupListeners",[33,2077,143],{"class":135},[33,2079,2080],{"class":164},"element",[33,2082,1412],{"class":135},[33,2084,2085,2087,2089,2091,2093,2095],{"class":35,"line":178},[33,2086,515],{"class":171},[33,2088,1572],{"class":201},[33,2090,521],{"class":171},[33,2092,1275],{"class":171},[33,2094,1579],{"class":139},[33,2096,1493],{"class":135},[33,2098,2099,2102,2104,2106,2108],{"class":35,"line":195},[33,2100,2101],{"class":135},"    element.",[33,2103,153],{"class":139},[33,2105,143],{"class":135},[33,2107,158],{"class":146},[33,2109,2110],{"class":135},", handler1, { signal: controller.signal });\n",[33,2112,2113,2115,2117,2119,2122],{"class":35,"line":211},[33,2114,2101],{"class":135},[33,2116,153],{"class":139},[33,2118,143],{"class":135},[33,2120,2121],{"class":146},"\"input\"",[33,2123,2124],{"class":135},", handler2, { signal: controller.signal });\n",[33,2126,2127,2129,2131,2133,2136],{"class":35,"line":216},[33,2128,2101],{"class":135},[33,2130,153],{"class":139},[33,2132,143],{"class":135},[33,2134,2135],{"class":146},"\"keydown\"",[33,2137,2138],{"class":135},", handler3, { signal: controller.signal });\n",[33,2140,2141,2143,2146,2148,2151,2153,2155],{"class":35,"line":245},[33,2142,1968],{"class":171},[33,2144,2145],{"class":135}," () ",[33,2147,172],{"class":171},[33,2149,2150],{"class":135}," controller.",[33,2152,1610],{"class":139},[33,2154,863],{"class":135},[33,2156,2157],{"class":39},"\u002F\u002F one call removes ALL three listeners\n",[33,2159,2160],{"class":35,"line":259},[33,2161,1477],{"class":135},[33,2163,2164,2166,2169,2171,2173],{"class":35,"line":272},[33,2165,1186],{"class":171},[33,2167,2168],{"class":201}," cleanup",[33,2170,521],{"class":171},[33,2172,2075],{"class":139},[33,2174,2175],{"class":135},"(form);\n",[33,2177,2178,2181],{"class":35,"line":277},[33,2179,2180],{"class":39},"\u002F\u002F Later: cleanup();",[33,2182,2183],{"class":39},"  \u002F\u002F removes all listeners at once (no individual removeEventListener calls)\n",[33,2185,2186],{"class":35,"line":283},[33,2187,123],{"emptyLinePlaceholder":122},[33,2189,2190],{"class":35,"line":289},[33,2191,2192],{"class":39},"\u002F\u002F ── Check if default was prevented ──\n",[33,2194,2195,2198,2200,2202,2204,2206,2208,2210,2212],{"class":35,"line":295},[33,2196,2197],{"class":135},"element.",[33,2199,153],{"class":139},[33,2201,143],{"class":135},[33,2203,158],{"class":146},[33,2205,161],{"class":135},[33,2207,165],{"class":164},[33,2209,168],{"class":135},[33,2211,172],{"class":171},[33,2213,175],{"class":135},[33,2215,2216,2218,2220,2222,2225],{"class":35,"line":301},[33,2217,541],{"class":171},[33,2219,544],{"class":135},[33,2221,547],{"class":171},[33,2223,2224],{"class":135},"e.defaultPrevented) {  ",[33,2226,2227],{"class":39},"\u002F\u002F another handler hasn't called preventDefault\n",[33,2229,2230,2233],{"class":35,"line":306},[33,2231,2232],{"class":139},"        doSomething",[33,2234,1493],{"class":135},[33,2236,2237],{"class":35,"line":312},[33,2238,793],{"class":135},[33,2240,2241],{"class":35,"line":341},[33,2242,382],{"class":135},[33,2244,2245],{"class":35,"line":354},[33,2246,123],{"emptyLinePlaceholder":122},[33,2248,2249],{"class":35,"line":367},[33,2250,2251],{"class":39},"\u002F\u002F ── Dispatch synthetic events (for testing or automation) ──\n",[33,2253,2254,2256,2259,2261,2263,2266,2268,2270,2273,2275,2278,2280],{"class":35,"line":373},[33,2255,1186],{"class":171},[33,2257,2258],{"class":201}," clickEvent",[33,2260,521],{"class":171},[33,2262,1275],{"class":171},[33,2264,2265],{"class":139}," MouseEvent",[33,2267,143],{"class":135},[33,2269,158],{"class":146},[33,2271,2272],{"class":135},", { bubbles: ",[33,2274,264],{"class":201},[33,2276,2277],{"class":135},", cancelable: ",[33,2279,264],{"class":201},[33,2281,1782],{"class":135},[33,2283,2284,2286,2288,2291],{"class":35,"line":379},[33,2285,2197],{"class":135},[33,2287,1355],{"class":139},[33,2289,2290],{"class":135},"(clickEvent);  ",[33,2292,2293],{"class":39},"\u002F\u002F triggers click listeners as if the user clicked\n",[15,2295,2297],{"id":2296},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,2299,2300],{"language":22},[24,2301,2303],{"className":26,"code":2302,"language":22,"meta":28,"style":28},"\u002F\u002F ── Arrow function event listeners can't be removed (no named reference) ──\nelement.addEventListener(\"click\", () => doSomething());\n\u002F\u002F Can't remove this — there's no reference to the anonymous function.\n\u002F\u002F Fix: use a named function (or AbortController):\nfunction handler() { doSomething(); }\nelement.addEventListener(\"click\", handler);\nelement.removeEventListener(\"click\", handler);  \u002F\u002F ✓ can remove\n\n\u002F\u002F ── removeEventListener must match the exact same function and options ──\nelement.addEventListener(\"click\", handler, { passive: true });\nelement.removeEventListener(\"click\", handler);  \u002F\u002F ⚠️ might not remove (options differ)\nelement.removeEventListener(\"click\", handler, { passive: true });  \u002F\u002F ✓ exact match\n\n\u002F\u002F ── `this` in event listeners ──\nelement.addEventListener(\"click\", function() {\n    console.log(this);  \u002F\u002F `this` is the element (for regular functions)\n});\nelement.addEventListener(\"click\", () => {\n    \u002F\u002F console.log(this);  \u002F\u002F `this` is NOT the element (arrow — lexical `this`)\n    \u002F\u002F Use e.currentTarget instead: console.log(e.currentTarget)\n});\n\n\u002F\u002F ── Events on disabled elements don't fire ──\n\u002F\u002F \u003Cbutton disabled> — click events don't fire on disabled elements\n\u002F\u002F Fix: wrap in a parent and listen on the parent (event delegation)\n\n\u002F\u002F ── passive listeners can't call preventDefault (it's a no-op) ──\nelement.addEventListener(\"touchmove\", (e) => {\n    e.preventDefault();  \u002F\u002F ⚠️ no-op! (passive: true ignores preventDefault)\n    \u002F\u002F Console warning: \"Unable to preventDefault inside passive event listener\"\n}, { passive: true });\n\n\u002F\u002F ── Custom events don't bubble by default ──\nconst event = new CustomEvent(\"myevent\");  \u002F\u002F bubbles: false (default)\n\u002F\u002F Must set { bubbles: true } to bubble up the DOM\n",[30,2304,2305,2310,2330,2335,2340,2356,2369,2386,2390,2395,2412,2427,2446,2450,2455,2472,2487,2491,2507,2515,2520,2524,2528,2533,2538,2543,2547,2552,2572,2583,2588,2596,2600,2605,2627],{"__ignoreMap":28},[33,2306,2307],{"class":35,"line":36},[33,2308,2309],{"class":39},"\u002F\u002F ── Arrow function event listeners can't be removed (no named reference) ──\n",[33,2311,2312,2314,2316,2318,2320,2322,2324,2327],{"class":35,"line":43},[33,2313,2197],{"class":135},[33,2315,153],{"class":139},[33,2317,143],{"class":135},[33,2319,158],{"class":146},[33,2321,453],{"class":135},[33,2323,172],{"class":171},[33,2325,2326],{"class":139}," doSomething",[33,2328,2329],{"class":135},"());\n",[33,2331,2332],{"class":35,"line":49},[33,2333,2334],{"class":39},"\u002F\u002F Can't remove this — there's no reference to the anonymous function.\n",[33,2336,2337],{"class":35,"line":55},[33,2338,2339],{"class":39},"\u002F\u002F Fix: use a named function (or AbortController):\n",[33,2341,2342,2344,2347,2350,2353],{"class":35,"line":61},[33,2343,2072],{"class":171},[33,2345,2346],{"class":139}," handler",[33,2348,2349],{"class":135},"() { ",[33,2351,2352],{"class":139},"doSomething",[33,2354,2355],{"class":135},"(); }\n",[33,2357,2358,2360,2362,2364,2366],{"class":35,"line":67},[33,2359,2197],{"class":135},[33,2361,153],{"class":139},[33,2363,143],{"class":135},[33,2365,158],{"class":146},[33,2367,2368],{"class":135},", handler);\n",[33,2370,2371,2373,2376,2378,2380,2383],{"class":35,"line":72},[33,2372,2197],{"class":135},[33,2374,2375],{"class":139},"removeEventListener",[33,2377,143],{"class":135},[33,2379,158],{"class":146},[33,2381,2382],{"class":135},", handler);  ",[33,2384,2385],{"class":39},"\u002F\u002F ✓ can remove\n",[33,2387,2388],{"class":35,"line":78},[33,2389,123],{"emptyLinePlaceholder":122},[33,2391,2392],{"class":35,"line":84},[33,2393,2394],{"class":39},"\u002F\u002F ── removeEventListener must match the exact same function and options ──\n",[33,2396,2397,2399,2401,2403,2405,2408,2410],{"class":35,"line":90},[33,2398,2197],{"class":135},[33,2400,153],{"class":139},[33,2402,143],{"class":135},[33,2404,158],{"class":146},[33,2406,2407],{"class":135},", handler, { passive: ",[33,2409,264],{"class":201},[33,2411,1782],{"class":135},[33,2413,2414,2416,2418,2420,2422,2424],{"class":35,"line":96},[33,2415,2197],{"class":135},[33,2417,2375],{"class":139},[33,2419,143],{"class":135},[33,2421,158],{"class":146},[33,2423,2382],{"class":135},[33,2425,2426],{"class":39},"\u002F\u002F ⚠️ might not remove (options differ)\n",[33,2428,2429,2431,2433,2435,2437,2439,2441,2443],{"class":35,"line":101},[33,2430,2197],{"class":135},[33,2432,2375],{"class":139},[33,2434,143],{"class":135},[33,2436,158],{"class":146},[33,2438,2407],{"class":135},[33,2440,264],{"class":201},[33,2442,1553],{"class":135},[33,2444,2445],{"class":39},"\u002F\u002F ✓ exact match\n",[33,2447,2448],{"class":35,"line":107},[33,2449,123],{"emptyLinePlaceholder":122},[33,2451,2452],{"class":35,"line":113},[33,2453,2454],{"class":39},"\u002F\u002F ── `this` in event listeners ──\n",[33,2456,2457,2459,2461,2463,2465,2467,2469],{"class":35,"line":119},[33,2458,2197],{"class":135},[33,2460,153],{"class":139},[33,2462,143],{"class":135},[33,2464,158],{"class":146},[33,2466,1406],{"class":135},[33,2468,2072],{"class":171},[33,2470,2471],{"class":135},"() {\n",[33,2473,2474,2476,2478,2480,2482,2484],{"class":35,"line":126},[33,2475,181],{"class":135},[33,2477,184],{"class":139},[33,2479,143],{"class":135},[33,2481,1460],{"class":201},[33,2483,205],{"class":135},[33,2485,2486],{"class":39},"\u002F\u002F `this` is the element (for regular functions)\n",[33,2488,2489],{"class":35,"line":132},[33,2490,382],{"class":135},[33,2492,2493,2495,2497,2499,2501,2503,2505],{"class":35,"line":178},[33,2494,2197],{"class":135},[33,2496,153],{"class":139},[33,2498,143],{"class":135},[33,2500,158],{"class":146},[33,2502,453],{"class":135},[33,2504,172],{"class":171},[33,2506,175],{"class":135},[33,2508,2509,2512],{"class":35,"line":195},[33,2510,2511],{"class":39},"    \u002F\u002F console.log(this);",[33,2513,2514],{"class":39},"  \u002F\u002F `this` is NOT the element (arrow — lexical `this`)\n",[33,2516,2517],{"class":35,"line":211},[33,2518,2519],{"class":39},"    \u002F\u002F Use e.currentTarget instead: console.log(e.currentTarget)\n",[33,2521,2522],{"class":35,"line":216},[33,2523,382],{"class":135},[33,2525,2526],{"class":35,"line":245},[33,2527,123],{"emptyLinePlaceholder":122},[33,2529,2530],{"class":35,"line":259},[33,2531,2532],{"class":39},"\u002F\u002F ── Events on disabled elements don't fire ──\n",[33,2534,2535],{"class":35,"line":272},[33,2536,2537],{"class":39},"\u002F\u002F \u003Cbutton disabled> — click events don't fire on disabled elements\n",[33,2539,2540],{"class":35,"line":277},[33,2541,2542],{"class":39},"\u002F\u002F Fix: wrap in a parent and listen on the parent (event delegation)\n",[33,2544,2545],{"class":35,"line":283},[33,2546,123],{"emptyLinePlaceholder":122},[33,2548,2549],{"class":35,"line":289},[33,2550,2551],{"class":39},"\u002F\u002F ── passive listeners can't call preventDefault (it's a no-op) ──\n",[33,2553,2554,2556,2558,2560,2562,2564,2566,2568,2570],{"class":35,"line":295},[33,2555,2197],{"class":135},[33,2557,153],{"class":139},[33,2559,143],{"class":135},[33,2561,1661],{"class":146},[33,2563,161],{"class":135},[33,2565,165],{"class":164},[33,2567,168],{"class":135},[33,2569,172],{"class":171},[33,2571,175],{"class":135},[33,2573,2574,2576,2578,2580],{"class":35,"line":301},[33,2575,858],{"class":135},[33,2577,1092],{"class":139},[33,2579,863],{"class":135},[33,2581,2582],{"class":39},"\u002F\u002F ⚠️ no-op! (passive: true ignores preventDefault)\n",[33,2584,2585],{"class":35,"line":306},[33,2586,2587],{"class":39},"    \u002F\u002F Console warning: \"Unable to preventDefault inside passive event listener\"\n",[33,2589,2590,2592,2594],{"class":35,"line":312},[33,2591,1693],{"class":135},[33,2593,264],{"class":201},[33,2595,1782],{"class":135},[33,2597,2598],{"class":35,"line":341},[33,2599,123],{"emptyLinePlaceholder":122},[33,2601,2602],{"class":35,"line":354},[33,2603,2604],{"class":39},"\u002F\u002F ── Custom events don't bubble by default ──\n",[33,2606,2607,2609,2611,2613,2615,2617,2619,2622,2624],{"class":35,"line":367},[33,2608,1186],{"class":171},[33,2610,1270],{"class":201},[33,2612,521],{"class":171},[33,2614,1275],{"class":171},[33,2616,1278],{"class":139},[33,2618,143],{"class":135},[33,2620,2621],{"class":146},"\"myevent\"",[33,2623,205],{"class":135},[33,2625,2626],{"class":39},"\u002F\u002F bubbles: false (default)\n",[33,2628,2629],{"class":35,"line":373},[33,2630,2631],{"class":39},"\u002F\u002F Must set { bubbles: true } to bubble up the DOM\n",[15,2633,2635],{"id":2634},"quick-quiz","🧠 Quick Quiz",[2637,2638,2639,2640,2643],"p",{},"In what order do the handlers fire when you click ",[30,2641,2642],{},"#inner","?",[20,2645,2647],{"language":2646},"html",[24,2648,2651],{"className":2649,"code":2650,"language":2646,"meta":28,"style":28},"language-html shiki shiki-themes github-light github-dark","\u003Cdiv id=\"outer\">\n    \u003Cdiv id=\"inner\">\u003C\u002Fdiv>\n\u003C\u002Fdiv>\n",[30,2652,2653,2672,2693],{"__ignoreMap":28},[33,2654,2655,2658,2662,2664,2666,2669],{"class":35,"line":36},[33,2656,2657],{"class":135},"\u003C",[33,2659,2661],{"class":2660},"sk71V","div",[33,2663,712],{"class":139},[33,2665,1423],{"class":135},[33,2667,2668],{"class":146},"\"outer\"",[33,2670,2671],{"class":135},">\n",[33,2673,2674,2677,2679,2681,2683,2686,2689,2691],{"class":35,"line":43},[33,2675,2676],{"class":135},"    \u003C",[33,2678,2661],{"class":2660},[33,2680,712],{"class":139},[33,2682,1423],{"class":135},[33,2684,2685],{"class":146},"\"inner\"",[33,2687,2688],{"class":135},">\u003C\u002F",[33,2690,2661],{"class":2660},[33,2692,2671],{"class":135},[33,2694,2695,2698,2700],{"class":35,"line":49},[33,2696,2697],{"class":135},"\u003C\u002F",[33,2699,2661],{"class":2660},[33,2701,2671],{"class":135},[2703,2704,2705],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}",{"title":28,"searchDepth":43,"depth":43,"links":2707},[2708,2709,2710,2712,2713,2714,2715,2716],{"id":17,"depth":43,"text":18},{"id":385,"depth":43,"text":386},{"id":800,"depth":43,"text":2711},"stopPropagation vs stopImmediatePropagation",{"id":1168,"depth":43,"text":1169},{"id":1618,"depth":43,"text":1619},{"id":1852,"depth":43,"text":1853},{"id":2296,"depth":43,"text":2297},{"id":2634,"depth":43,"text":2635},"Deep-dive into the DOM event model: capture and bubble phases, event delegation, stopPropagation vs stopImmediatePropagation, Custom Events, AbortController for cleanup, and passive listeners. Code-first reference for senior engineers.","md",{},"\u002Fjs\u002F20-events",{"title":5,"description":2717},"js\u002F20-events","dzQDsF5mF3uUkZ9mO3jXsB_d065dGsTeHbqOsMSXfWk",1789924649417]