[{"data":1,"prerenderedAt":3506},["ShallowReactive",2],{"page-\u002Fjs\u002F19-the-dom":3},{"id":4,"title":5,"body":6,"description":170,"extension":3500,"meta":3501,"navigation":51,"path":3502,"seo":3503,"stem":3504,"__hash__":3505},"content\u002Fjs\u002F19-the-dom.md","19 — The DOM",{"type":7,"value":8,"toc":3462},"minimark",[9,13,151,420,634,825,977,1194,1271,1437,1646,1842,2021,2124,2333,2468,2660,2804,3063,3186,3372,3458],[10,11,5],"h1",{"id":12},"_19-the-dom",[14,15,16,38,99],"question-wrapper",{},[17,18,20,21,25,26,29,30,33,34,37],"h3",{"id":19},"q1-a-page-has-multiple-li-classitem-elements-inside-a-ul-what-is-the-key-structural-difference-between-the-results-of-documentqueryselectorallitem-and-documentgetelementsbyclassnameitem","Q1. A page has multiple ",[22,23,24],"code",{},"\u003Cli class=\"item\">"," elements inside a ",[22,27,28],{},"\u003Cul>",". What is the key structural difference between the results of ",[22,31,32],{},"document.querySelectorAll('.item')"," and ",[22,35,36],{},"document.getElementsByClassName('item')","?",[39,40,43,58,79,93],"ul",{"className":41},[42],"contains-task-list",[44,45,48,53,54,57],"li",{"className":46},[47],"task-list-item",[49,50],"input",{"disabled":51,"type":52},true,"checkbox"," Both return a live ",[22,55,56],{},"HTMLCollection"," that updates automatically as the DOM changes",[44,59,61,63,64,67,68,71,72,75,76,78],{"className":60},[47],[49,62],{"disabled":51,"type":52}," ",[22,65,66],{},"querySelectorAll"," returns a static ",[22,69,70],{},"NodeList"," snapshot taken at call time; ",[22,73,74],{},"getElementsByClassName"," returns a live ",[22,77,56],{}," that reflects later DOM changes",[44,80,82,63,84,75,86,88,89,67,91],{"className":81},[47],[49,83],{"disabled":51,"type":52},[22,85,66],{},[22,87,56],{},"; ",[22,90,74],{},[22,92,70],{},[44,94,96,98],{"className":95},[47],[49,97],{"disabled":51,"type":52}," Both return static arrays that never update, regardless of which method is used",[100,101,102,106,122],"details",{},[103,104,105],"summary",{},"Show Answer",[107,108,109,113,114,67,116,71,118,75,120,78],"p",{},[110,111,112],"strong",{},"Answer:"," B — ",[22,115,66],{},[22,117,70],{},[22,119,74],{},[22,121,56],{},[107,123,124,63,127,129,130,133,134,136,137,139,140,143,144,147,148,150],{},[110,125,126],{},"Explanation:",[22,128,66],{}," (and ",[22,131,132],{},"querySelector",") always builds a static ",[22,135,70],{}," — a frozen snapshot of what matched at the moment the query ran, unaffected by later DOM mutations. The older ",[22,138,74],{},", ",[22,141,142],{},"getElementsByTagName",", and ",[22,145,146],{},"getElementsByName"," methods return live ",[22,149,56],{},"s, which behave like a saved query that automatically re-evaluates as the document changes. Option C reverses the two — a very common mix-up. Options A and D collapse a real, load-bearing distinction that later questions build on.",[14,152,154,162,311,371],{"language":153},"javascript",[17,155,157,158,161],{"id":156},"q2-what-does-the-second-consolelog-print","Q2. What does the second ",[22,159,160],{},"console.log"," print?",[163,164,165],"code-wrapper",{"language":153},[166,167,171],"pre",{"className":168,"code":169,"language":153,"meta":170,"style":170},"language-javascript shiki shiki-themes github-light github-dark","const items = document.querySelectorAll('.item');\nconsole.log(items.length); \u002F\u002F 3\n\nconst li = document.createElement('li');\nli.className = 'item';\ndocument.querySelector('ul').appendChild(li);\n\nconsole.log(items.length);\n","",[22,172,173,206,228,234,256,271,293,298],{"__ignoreMap":170},[174,175,178,182,186,189,193,196,199,203],"span",{"class":176,"line":177},"line",1,[174,179,181],{"class":180},"svdQ7","const",[174,183,185],{"class":184},"snvgF"," items",[174,187,188],{"class":180}," =",[174,190,192],{"class":191},"ssxIu"," document.",[174,194,66],{"class":195},"sIsaT",[174,197,198],{"class":191},"(",[174,200,202],{"class":201},"sJ6F3","'.item'",[174,204,205],{"class":191},");\n",[174,207,209,212,215,218,221,224],{"class":176,"line":208},2,[174,210,211],{"class":191},"console.",[174,213,214],{"class":195},"log",[174,216,217],{"class":191},"(items.",[174,219,220],{"class":184},"length",[174,222,223],{"class":191},"); ",[174,225,227],{"class":226},"sdCPZ","\u002F\u002F 3\n",[174,229,231],{"class":176,"line":230},3,[174,232,233],{"emptyLinePlaceholder":51},"\n",[174,235,237,239,242,244,246,249,251,254],{"class":176,"line":236},4,[174,238,181],{"class":180},[174,240,241],{"class":184}," li",[174,243,188],{"class":180},[174,245,192],{"class":191},[174,247,248],{"class":195},"createElement",[174,250,198],{"class":191},[174,252,253],{"class":201},"'li'",[174,255,205],{"class":191},[174,257,259,262,265,268],{"class":176,"line":258},5,[174,260,261],{"class":191},"li.className ",[174,263,264],{"class":180},"=",[174,266,267],{"class":201}," 'item'",[174,269,270],{"class":191},";\n",[174,272,274,277,279,281,284,287,290],{"class":176,"line":273},6,[174,275,276],{"class":191},"document.",[174,278,132],{"class":195},[174,280,198],{"class":191},[174,282,283],{"class":201},"'ul'",[174,285,286],{"class":191},").",[174,288,289],{"class":195},"appendChild",[174,291,292],{"class":191},"(li);\n",[174,294,296],{"class":176,"line":295},7,[174,297,233],{"emptyLinePlaceholder":51},[174,299,301,303,305,307,309],{"class":176,"line":300},8,[174,302,211],{"class":191},[174,304,214],{"class":195},[174,306,217],{"class":191},[174,308,220],{"class":184},[174,310,205],{"class":191},[39,312,314,330,343,355],{"className":313},[42],[44,315,317,63,319,322,323,326,327,329],{"className":316},[47],[49,318],{"disabled":51,"type":52},[22,320,321],{},"3"," — ",[22,324,325],{},"items"," is a static ",[22,328,70],{}," captured at query time and does not reflect the element added afterward",[44,331,333,63,335,322,338,340,341],{"className":332},[47],[49,334],{"disabled":51,"type":52},[22,336,337],{},"4",[22,339,66],{}," always returns a live collection, just like ",[22,342,74],{},[44,344,346,63,348,350,351,354],{"className":345},[47],[49,347],{"disabled":51,"type":52},[22,349,321],{},", then it throws a ",[22,352,353],{},"TypeError"," on the second access because the DOM changed underneath it",[44,356,358,63,360,363,364,367,368,370],{"className":357},[47],[49,359],{"disabled":51,"type":52},[22,361,362],{},"undefined"," — reassigning ",[22,365,366],{},"document.querySelector('ul')"," invalidates the earlier ",[22,369,325],{}," reference",[100,372,373,375,386],{},[103,374,105],{},[107,376,377,379,380,322,382,326,384,329],{},[110,378,112],{}," A — ",[22,381,321],{},[22,383,325],{},[22,385,70],{},[107,387,388,63,390,392,393,396,397,400,401,403,404,407,408,410,411,413,414,416,417,419],{},[110,389,126],{},[22,391,66],{}," freezes its result at call time. The new ",[22,394,395],{},"\u003Cli>"," appended afterward matches the original ",[22,398,399],{},".item"," selector conceptually, but ",[22,402,325],{}," was never wired up to re-run that query — it's just an array-like snapshot, so its ",[22,405,406],{},".length"," stays ",[22,409,321],{}," forever unless you call ",[22,412,66],{}," again. Option B is the classic live-collection assumption bleeding over from ",[22,415,74],{}," (contrasted directly in Q3). Options C and D invent errors that don't occur — reading a stale static ",[22,418,70],{}," is completely safe, just potentially outdated.",[14,421,422,434,538,586],{"language":153},[17,423,425,426,428,429,431,432,161],{"id":424},"q3-using-getelementsbyclassname-instead-of-queryselectorall-for-the-same-scenario-what-does-the-second-consolelog-print","Q3. Using ",[22,427,74],{}," instead of ",[22,430,66],{}," for the same scenario, what does the second ",[22,433,160],{},[163,435,436],{"language":153},[166,437,439],{"className":168,"code":438,"language":153,"meta":170,"style":170},"const items = document.getElementsByClassName('item');\nconsole.log(items.length); \u002F\u002F 3\n\nconst li = document.createElement('li');\nli.className = 'item';\ndocument.querySelector('ul').appendChild(li);\n\nconsole.log(items.length);\n",[22,440,441,460,474,478,496,506,522,526],{"__ignoreMap":170},[174,442,443,445,447,449,451,453,455,458],{"class":176,"line":177},[174,444,181],{"class":180},[174,446,185],{"class":184},[174,448,188],{"class":180},[174,450,192],{"class":191},[174,452,74],{"class":195},[174,454,198],{"class":191},[174,456,457],{"class":201},"'item'",[174,459,205],{"class":191},[174,461,462,464,466,468,470,472],{"class":176,"line":208},[174,463,211],{"class":191},[174,465,214],{"class":195},[174,467,217],{"class":191},[174,469,220],{"class":184},[174,471,223],{"class":191},[174,473,227],{"class":226},[174,475,476],{"class":176,"line":230},[174,477,233],{"emptyLinePlaceholder":51},[174,479,480,482,484,486,488,490,492,494],{"class":176,"line":236},[174,481,181],{"class":180},[174,483,241],{"class":184},[174,485,188],{"class":180},[174,487,192],{"class":191},[174,489,248],{"class":195},[174,491,198],{"class":191},[174,493,253],{"class":201},[174,495,205],{"class":191},[174,497,498,500,502,504],{"class":176,"line":258},[174,499,261],{"class":191},[174,501,264],{"class":180},[174,503,267],{"class":201},[174,505,270],{"class":191},[174,507,508,510,512,514,516,518,520],{"class":176,"line":273},[174,509,276],{"class":191},[174,511,132],{"class":195},[174,513,198],{"class":191},[174,515,283],{"class":201},[174,517,286],{"class":191},[174,519,289],{"class":195},[174,521,292],{"class":191},[174,523,524],{"class":176,"line":295},[174,525,233],{"emptyLinePlaceholder":51},[174,527,528,530,532,534,536],{"class":176,"line":300},[174,529,211],{"class":191},[174,531,214],{"class":195},[174,533,217],{"class":191},[174,535,220],{"class":184},[174,537,205],{"class":191},[39,539,541,551,561,573],{"className":540},[42],[44,542,544,63,546,322,548,550],{"className":543},[47],[49,545],{"disabled":51,"type":52},[22,547,321],{},[22,549,56],{}," becomes static once it's assigned to a variable",[44,552,554,63,556,322,558,560],{"className":553},[47],[49,555],{"disabled":51,"type":52},[22,557,362],{},[22,559,325],{}," becomes stale as soon as the DOM mutates",[44,562,564,63,566,322,568,75,570,572],{"className":563},[47],[49,565],{"disabled":51,"type":52},[22,567,337],{},[22,569,74],{},[22,571,56],{}," that automatically reflects DOM changes matching the query",[44,574,576,63,578,580,581,583,584],{"className":575},[47],[49,577],{"disabled":51,"type":52},[22,579,321],{},", then it throws because ",[22,582,325],{}," was declared with ",[22,585,181],{},[100,587,588,590,601],{},[103,589,105],{},[107,591,592,594,595,322,597,75,599,572],{},[110,593,112],{}," C — ",[22,596,337],{},[22,598,74],{},[22,600,56],{},[107,602,603,605,606,608,609,611,612,615,616,618,619,623,624,626,627,630,631,633],{},[110,604,126],{}," A live ",[22,607,56],{}," isn't a snapshot at all — it's a view maintained by the DOM implementation that re-evaluates its matching criteria whenever the tree changes, so appending a new ",[22,610,399],{}," node immediately shows up in ",[22,613,614],{},"items.length"," with zero extra code. Option D confuses ",[22,617,181],{},"'s restriction (you can't ",[620,621,622],"em",{},"reassign"," the ",[22,625,325],{}," binding) with the collection's ",[620,628,629],{},"internal"," mutability, which ",[22,632,181],{}," has no effect on — the object itself is still free to change. Option A is exactly the incorrect assumption this question tests. Option B invents behavior that doesn't happen.",[14,635,636,644,728,770],{"language":153},[17,637,639,640,643],{"id":638},"q4-given-4-elements-with-class-flagged-what-actually-happens-when-this-loop-runs","Q4. Given 4 elements with class ",[22,641,642],{},"\"flagged\"",", what actually happens when this loop runs?",[163,645,646],{"language":153},[166,647,649],{"className":168,"code":648,"language":153,"meta":170,"style":170},"const items = document.getElementsByClassName('flagged');\nfor (let i = 0; i \u003C items.length; i++) {\n  items[i].classList.remove('flagged');\n}\n",[22,650,651,670,709,723],{"__ignoreMap":170},[174,652,653,655,657,659,661,663,665,668],{"class":176,"line":177},[174,654,181],{"class":180},[174,656,185],{"class":184},[174,658,188],{"class":180},[174,660,192],{"class":191},[174,662,74],{"class":195},[174,664,198],{"class":191},[174,666,667],{"class":201},"'flagged'",[174,669,205],{"class":191},[174,671,672,675,678,681,684,686,689,692,695,698,700,703,706],{"class":176,"line":208},[174,673,674],{"class":180},"for",[174,676,677],{"class":191}," (",[174,679,680],{"class":180},"let",[174,682,683],{"class":191}," i ",[174,685,264],{"class":180},[174,687,688],{"class":184}," 0",[174,690,691],{"class":191},"; i ",[174,693,694],{"class":180},"\u003C",[174,696,697],{"class":191}," items.",[174,699,220],{"class":184},[174,701,702],{"class":191},"; i",[174,704,705],{"class":180},"++",[174,707,708],{"class":191},") {\n",[174,710,711,714,717,719,721],{"class":176,"line":230},[174,712,713],{"class":191},"  items[i].classList.",[174,715,716],{"class":195},"remove",[174,718,198],{"class":191},[174,720,667],{"class":201},[174,722,205],{"class":191},[174,724,725],{"class":176,"line":236},[174,726,727],{"class":191},"}\n",[39,729,731,737,750,756],{"className":730},[42],[44,732,734,736],{"className":733},[47],[49,735],{"disabled":51,"type":52}," It removes the class from all 4 elements as expected, then exits cleanly",[44,738,740,742,743,746,747,749],{"className":739},[47],[49,741],{"disabled":51,"type":52}," It throws a ",[22,744,745],{},"RangeError"," because ",[22,748,614],{}," changes while the loop is running",[44,751,753,755],{"className":752},[47],[49,754],{"disabled":51,"type":52}," It removes the class from only the last 2 elements, leaving the first 2 unchanged",[44,757,759,761,762,765,766,769],{"className":758},[47],[49,760],{"disabled":51,"type":52}," It removes the class from only every other element — removing ",[22,763,764],{},"flagged"," from ",[22,767,768],{},"items[0]"," shrinks the live collection and shifts the next element into index 0, which the incrementing loop then skips over",[100,771,772,774,783],{},[103,773,105],{},[107,775,776,778,779,765,781,769],{},[110,777,112],{}," D — It removes the class from only every other element — removing ",[22,780,764],{},[22,782,768],{},[107,784,785,63,787,75,789,791,792,794,795,797,798,800,801,804,805,808,809,812,813,816,817,820,821,824],{},[110,786,126],{},[22,788,74],{},[22,790,56],{}," (Q3), so the moment ",[22,793,768],{}," loses the ",[22,796,764],{}," class, it drops out of the collection entirely — every remaining matched element shifts down one index, and ",[22,799,614],{}," shrinks by one. But the loop's ",[22,802,803],{},"i"," still increments to ",[22,806,807],{},"1",", which now points at what used to be ",[22,810,811],{},"items[2]",", silently skipping the element that shifted into slot ",[22,814,815],{},"0",". ",[110,818,819],{},"Debug:"," this is the canonical live-collection mutation bug — fix it by iterating a static copy (",[22,822,823],{},"Array.from(items)","), snapshotting the length up front, or walking backwards from the end. Options A and B are the naive assumptions; C names a plausible-sounding but incorrect specific outcome.",[14,826,827,839,906],{},[17,828,830,831,834,835,838],{"id":829},"q5-a-developer-writes-documentgetelementsbytagnameimgforeachimg-imgloading-lazy-and-gets-typeerror-itemsforeach-is-not-a-function-why-and-whats-the-correct-fix","Q5. A developer writes ",[22,832,833],{},"document.getElementsByTagName('img').forEach(img => img.loading = 'lazy')"," and gets ",[22,836,837],{},"TypeError: items.forEach is not a function",". Why, and what's the correct fix?",[39,840,842,855,881,894],{"className":841},[42],[44,843,845,63,847,850,851,854],{"className":844},[47],[49,846],{"disabled":51,"type":52},[22,848,849],{},"forEach"," was removed from all DOM collections in newer browsers; a ",[22,852,853],{},"for...of"," loop is now required universally",[44,856,858,63,860,862,863,865,866,868,869,765,871,873,874,877,878],{"className":857},[47],[49,859],{"disabled":51,"type":52},[22,861,56],{}," (returned by ",[22,864,142],{},") never implemented ",[22,867,849],{},", unlike ",[22,870,70],{},[22,872,66],{},"; convert it first with ",[22,875,876],{},"Array.from(collection)"," or the spread ",[22,879,880],{},"[...collection]",[44,882,884,886,887,890,891,893],{"className":883},[47],[49,885],{"disabled":51,"type":52}," The code should call ",[22,888,889],{},".values().forEach(...)",", because ",[22,892,56],{}," only exposes an iterator, not array methods",[44,895,897,63,899,902,903,905],{"className":896},[47],[49,898],{"disabled":51,"type":52},[22,900,901],{},"img"," elements are specifically excluded from ",[22,904,849],{}," for security reasons; other tags work fine",[100,907,908,910,928],{},[103,909,105],{},[107,911,912,113,914,862,916,865,918,868,920,765,922,873,924,877,926],{},[110,913,112],{},[22,915,56],{},[22,917,142],{},[22,919,849],{},[22,921,70],{},[22,923,66],{},[22,925,876],{},[22,927,880],{},[107,929,930,63,932,935,936,938,939,941,942,944,945,947,948,951,952,955,956,958,959,961,962,139,964,139,967,970,971,973,974,976],{},[110,931,126],{},[22,933,934],{},"NodeList.prototype"," was given ",[22,937,849],{}," directly, so a ",[22,940,66],{}," result supports it out of the box. ",[22,943,56],{}," is a different, older interface exposing only ",[22,946,220],{},", indexed access, and ",[22,949,950],{},"namedItem"," — it was never extended with array methods, so calling ",[22,953,954],{},".forEach"," on it throws. ",[22,957,876],{}," or ",[22,960,880],{}," produces a real array with the full method set (",[22,963,849],{},[22,965,966],{},"map",[22,968,969],{},"filter",", and so on). Option A is false — ",[22,972,70],{}," still has ",[22,975,849],{},". Option C invents a nonexistent API surface. Option D is nonsensical; there's no tag-based restriction.",[14,978,979,989,1080,1146],{"language":153},[17,980,982,983,985,986,988],{"id":981},"q6-what-does-the-second-consolelog-print-and-why-does-it-complicate-the-nodelist-static-takeaway-from-q2","Q6. What does the second ",[22,984,160],{}," print, and why does it complicate the \"",[22,987,70],{}," = static\" takeaway from Q2?",[163,990,991],{"language":153},[166,992,994],{"className":168,"code":993,"language":153,"meta":170,"style":170},"const container = document.querySelector('#box');\nconst nodes = container.childNodes;\nconsole.log(nodes.length); \u002F\u002F 2\n\ncontainer.appendChild(document.createElement('span'));\nconsole.log(nodes.length);\n",[22,995,996,1016,1028,1044,1048,1068],{"__ignoreMap":170},[174,997,998,1000,1003,1005,1007,1009,1011,1014],{"class":176,"line":177},[174,999,181],{"class":180},[174,1001,1002],{"class":184}," container",[174,1004,188],{"class":180},[174,1006,192],{"class":191},[174,1008,132],{"class":195},[174,1010,198],{"class":191},[174,1012,1013],{"class":201},"'#box'",[174,1015,205],{"class":191},[174,1017,1018,1020,1023,1025],{"class":176,"line":208},[174,1019,181],{"class":180},[174,1021,1022],{"class":184}," nodes",[174,1024,188],{"class":180},[174,1026,1027],{"class":191}," container.childNodes;\n",[174,1029,1030,1032,1034,1037,1039,1041],{"class":176,"line":230},[174,1031,211],{"class":191},[174,1033,214],{"class":195},[174,1035,1036],{"class":191},"(nodes.",[174,1038,220],{"class":184},[174,1040,223],{"class":191},[174,1042,1043],{"class":226},"\u002F\u002F 2\n",[174,1045,1046],{"class":176,"line":236},[174,1047,233],{"emptyLinePlaceholder":51},[174,1049,1050,1053,1055,1058,1060,1062,1065],{"class":176,"line":258},[174,1051,1052],{"class":191},"container.",[174,1054,289],{"class":195},[174,1056,1057],{"class":191},"(document.",[174,1059,248],{"class":195},[174,1061,198],{"class":191},[174,1063,1064],{"class":201},"'span'",[174,1066,1067],{"class":191},"));\n",[174,1069,1070,1072,1074,1076,1078],{"class":176,"line":273},[174,1071,211],{"class":191},[174,1073,214],{"class":195},[174,1075,1036],{"class":191},[174,1077,220],{"class":184},[174,1079,205],{"class":191},[39,1081,1083,1104,1119,1136],{"className":1082},[42],[44,1084,1086,63,1088,1090,1091,1093,1094,1097,1098,63,1101,1103],{"className":1085},[47],[49,1087],{"disabled":51,"type":52},[22,1089,321],{}," — unlike ",[22,1092,66],{},"'s result, ",[22,1095,1096],{},".childNodes"," returns a ",[620,1099,1100],{},"live",[22,1102,70],{}," that updates as children are added or removed",[44,1105,1107,63,1109,1112,1113,1115,1116,1118],{"className":1106},[47],[49,1108],{"disabled":51,"type":52},[22,1110,1111],{},"2"," — every ",[22,1114,70],{},", including ",[22,1117,1096],{},", is a static snapshot",[44,1120,1122,63,1124,1126,1127,1129,1130,1132,1133,1135],{"className":1121},[47],[49,1123],{"disabled":51,"type":52},[22,1125,321],{}," — because ",[22,1128,1096],{}," actually returns an ",[22,1131,56],{},", not a ",[22,1134,70],{},", despite its name",[44,1137,1139,1141,1142,1145],{"className":1138},[47],[49,1140],{"disabled":51,"type":52}," It throws, because ",[22,1143,1144],{},"nodes"," was assigned before the DOM mutation occurred",[100,1147,1148,1150,1164],{},[103,1149,105],{},[107,1151,1152,379,1154,1090,1156,1093,1158,1097,1160,63,1162,1103],{},[110,1153,112],{},[22,1155,321],{},[22,1157,66],{},[22,1159,1096],{},[620,1161,1100],{},[22,1163,70],{},[107,1165,1166,1168,1169,1172,1173,1175,1176,1178,1179,1181,1182,1184,1185,1187,1188,1190,1191,1193],{},[110,1167,126],{}," \"Static vs. live\" is a property of ",[620,1170,1171],{},"how a collection is produced",", not of the ",[22,1174,70],{}," type itself. ",[22,1177,66],{}," is specified to always build a static snapshot, but other DOM properties — ",[22,1180,1096],{}," chief among them — are specified to return a live ",[22,1183,70],{}," that tracks the tree in real time. So \"",[22,1186,70],{},"\" and \"static\" are not synonyms, which trips up anyone who over-generalizes from ",[22,1189,66],{}," alone. Option C is wrong on the interface type — it genuinely is a ",[22,1192,70],{},", just a live one. Option B is the over-generalization this question targets. Option D fabricates an error that doesn't occur.",[14,1195,1196,1200,1235],{},[17,1197,1199],{"id":1198},"q7-in-browser-rendering-what-is-the-actual-difference-between-a-reflow-layout-and-a-repaint","Q7. In browser rendering, what is the actual difference between a \"reflow\" (layout) and a \"repaint\"?",[39,1201,1203,1213,1219,1225],{"className":1202},[42],[44,1204,1206,1208,1209,1212],{"className":1205},[47],[49,1207],{"disabled":51,"type":52}," Reflow only affects ",[22,1210,1211],{},"\u003Ccanvas>"," elements; repaint affects every other element",[44,1214,1216,1218],{"className":1215},[47],[49,1217],{"disabled":51,"type":52}," They are two names for the exact same browser operation",[44,1220,1222,1224],{"className":1221},[47],[49,1223],{"disabled":51,"type":52}," Reflow recalculates element geometry and position, and can cascade to affect ancestors, descendants, and siblings; repaint only redraws pixels (e.g., a color change) without recomputing any layout, which makes it cheaper",[44,1226,1228,1230,1231,1234],{"className":1227},[47],[49,1229],{"disabled":51,"type":52}," Repaint happens on every ",[22,1232,1233],{},"scroll"," event; reflow only ever happens once, on initial page load",[100,1236,1237,1239,1244],{},[103,1238,105],{},[107,1240,1241,1243],{},[110,1242,112],{}," C — Reflow recalculates element geometry and position, and can cascade to affect ancestors, descendants, and siblings; repaint only redraws pixels (e.g., a color change) without recomputing any layout, which makes it cheaper",[107,1245,1246,1248,1249,1252,1253,139,1256,1259,1260,958,1263,1266,1267,1270],{},[110,1247,126],{}," Reflow (layout) computes size and position for elements in the render tree, and because layout is fundamentally a tree-wide computation, a change to one element's box can ripple outward to affect its neighbors. Repaint (paint) only redraws the ",[620,1250,1251],{},"appearance"," of already-laid-out pixels — ",[22,1254,1255],{},"background-color",[22,1257,1258],{},"visibility",", and similar properties — reusing the existing geometry, so it skips the expensive remeasurement step entirely. Changing something like ",[22,1261,1262],{},"width",[22,1264,1265],{},"left"," triggers a reflow (and a subsequent repaint); changing only ",[22,1268,1269],{},"color"," triggers just a repaint. Options A and D fabricate rules that don't reflect how the rendering pipeline actually works; option B collapses a distinction that matters a great deal for performance, as the next questions show.",[14,1272,1273,1277,1350,1394],{"language":153},[17,1274,1276],{"id":1275},"q8-why-is-this-loop-considered-layout-thrashing-and-what-makes-it-especially-expensive","Q8. Why is this loop considered \"layout thrashing,\" and what makes it especially expensive?",[163,1278,1279],{"language":153},[166,1280,1282],{"className":168,"code":1281,"language":153,"meta":170,"style":170},"const boxes = document.querySelectorAll('.box');\nboxes.forEach(box => {\n  box.style.width = '200px';\n  console.log(box.offsetHeight);\n});\n",[22,1283,1284,1304,1323,1335,1345],{"__ignoreMap":170},[174,1285,1286,1288,1291,1293,1295,1297,1299,1302],{"class":176,"line":177},[174,1287,181],{"class":180},[174,1289,1290],{"class":184}," boxes",[174,1292,188],{"class":180},[174,1294,192],{"class":191},[174,1296,66],{"class":195},[174,1298,198],{"class":191},[174,1300,1301],{"class":201},"'.box'",[174,1303,205],{"class":191},[174,1305,1306,1309,1311,1313,1317,1320],{"class":176,"line":208},[174,1307,1308],{"class":191},"boxes.",[174,1310,849],{"class":195},[174,1312,198],{"class":191},[174,1314,1316],{"class":1315},"sCrzJ","box",[174,1318,1319],{"class":180}," =>",[174,1321,1322],{"class":191}," {\n",[174,1324,1325,1328,1330,1333],{"class":176,"line":230},[174,1326,1327],{"class":191},"  box.style.width ",[174,1329,264],{"class":180},[174,1331,1332],{"class":201}," '200px'",[174,1334,270],{"class":191},[174,1336,1337,1340,1342],{"class":176,"line":236},[174,1338,1339],{"class":191},"  console.",[174,1341,214],{"class":195},[174,1343,1344],{"class":191},"(box.offsetHeight);\n",[174,1346,1347],{"class":176,"line":258},[174,1348,1349],{"class":191},"});\n",[39,1351,1353,1362,1371,1379],{"className":1352},[42],[44,1354,1356,63,1358,1361],{"className":1355},[47],[49,1357],{"disabled":51,"type":52},[22,1359,1360],{},"offsetHeight"," is deprecated and logs a console warning on every access",[44,1363,1365,63,1367,1370],{"className":1364},[47],[49,1366],{"disabled":51,"type":52},[22,1368,1369],{},".style.width"," writes are batched automatically by the browser, so this pattern is actually efficient as written",[44,1372,1374,63,1376,1378],{"className":1373},[47],[49,1375],{"disabled":51,"type":52},[22,1377,66],{}," re-runs the CSS selector engine on every loop iteration, which is the real cost here",[44,1380,1382,1384,1385,1387,1388,1390,1391],{"className":1381},[47],[49,1383],{"disabled":51,"type":52}," Writing ",[22,1386,1369],{}," invalidates the cached layout, and immediately reading ",[22,1389,1360],{}," forces the browser to synchronously recompute layout right then instead of deferring it — repeating this write-then-read pattern for every element forces a full synchronous reflow on ",[620,1392,1393],{},"every single iteration",[100,1395,1396,1398,1409],{},[103,1397,105],{},[107,1399,1400,1402,1403,1387,1405,1390,1407],{},[110,1401,112],{}," D — Writing ",[22,1404,1369],{},[22,1406,1360],{},[620,1408,1393],{},[107,1410,1411,1413,1414,139,1416,139,1419,139,1422,1425,1426,1429,1430,1432,1433,1436],{},[110,1412,126],{}," Browsers normally batch layout-invalidating writes and defer the actual recalculation until it's genuinely needed (typically right before the next paint). But certain \"layout-dependent\" reads — ",[22,1415,1360],{},[22,1417,1418],{},"offsetWidth",[22,1420,1421],{},"getBoundingClientRect()",[22,1423,1424],{},"scrollTop",", computed styles — force an immediate, synchronous flush of any pending layout work so the browser can hand back an accurate number. Interleaving a write then a read inside a loop defeats that batching completely, forcing ",[620,1427,1428],{},"N"," synchronous reflows for ",[620,1431,1428],{}," elements instead of just one. ",[110,1434,1435],{},"Performance:"," this \"layout thrashing\" pattern is one of the most common real-world sources of janky UI code. Options A and C are fabricated costs; option B is the literal opposite of what happens in this snippet.",[14,1438,1439,1443,1564,1610],{"language":153},[17,1440,1442],{"id":1441},"q9-how-does-this-rewrite-of-the-q8-pattern-avoid-layout-thrashing","Q9. How does this rewrite of the Q8 pattern avoid layout thrashing?",[163,1444,1445],{"language":153},[166,1446,1448],{"className":168,"code":1447,"language":153,"meta":170,"style":170},"const boxes = document.querySelectorAll('.box');\n\nconst heights = [];\nboxes.forEach(box => heights.push(box.offsetHeight));\n\nboxes.forEach((box, i) => {\n  box.style.width = heights[i] > 100 ? '200px' : '100px';\n});\n",[22,1449,1450,1468,1472,1484,1505,1509,1532,1560],{"__ignoreMap":170},[174,1451,1452,1454,1456,1458,1460,1462,1464,1466],{"class":176,"line":177},[174,1453,181],{"class":180},[174,1455,1290],{"class":184},[174,1457,188],{"class":180},[174,1459,192],{"class":191},[174,1461,66],{"class":195},[174,1463,198],{"class":191},[174,1465,1301],{"class":201},[174,1467,205],{"class":191},[174,1469,1470],{"class":176,"line":208},[174,1471,233],{"emptyLinePlaceholder":51},[174,1473,1474,1476,1479,1481],{"class":176,"line":230},[174,1475,181],{"class":180},[174,1477,1478],{"class":184}," heights",[174,1480,188],{"class":180},[174,1482,1483],{"class":191}," [];\n",[174,1485,1486,1488,1490,1492,1494,1496,1499,1502],{"class":176,"line":236},[174,1487,1308],{"class":191},[174,1489,849],{"class":195},[174,1491,198],{"class":191},[174,1493,1316],{"class":1315},[174,1495,1319],{"class":180},[174,1497,1498],{"class":191}," heights.",[174,1500,1501],{"class":195},"push",[174,1503,1504],{"class":191},"(box.offsetHeight));\n",[174,1506,1507],{"class":176,"line":258},[174,1508,233],{"emptyLinePlaceholder":51},[174,1510,1511,1513,1515,1518,1520,1522,1524,1527,1530],{"class":176,"line":273},[174,1512,1308],{"class":191},[174,1514,849],{"class":195},[174,1516,1517],{"class":191},"((",[174,1519,1316],{"class":1315},[174,1521,139],{"class":191},[174,1523,803],{"class":1315},[174,1525,1526],{"class":191},") ",[174,1528,1529],{"class":180},"=>",[174,1531,1322],{"class":191},[174,1533,1534,1536,1538,1541,1544,1547,1550,1552,1555,1558],{"class":176,"line":295},[174,1535,1327],{"class":191},[174,1537,264],{"class":180},[174,1539,1540],{"class":191}," heights[i] ",[174,1542,1543],{"class":180},">",[174,1545,1546],{"class":184}," 100",[174,1548,1549],{"class":180}," ?",[174,1551,1332],{"class":201},[174,1553,1554],{"class":180}," :",[174,1556,1557],{"class":201}," '100px'",[174,1559,270],{"class":191},[174,1561,1562],{"class":176,"line":300},[174,1563,1349],{"class":191},[39,1565,1567,1576,1590,1602],{"className":1566},[42],[44,1568,1570,1572,1573,1575],{"className":1569},[47],[49,1571],{"disabled":51,"type":52}," It doesn't actually help — reading ",[22,1574,1360],{}," in the first loop still forces a reflow for every element, regardless of write timing",[44,1577,1579,1581,1582,1585,1586,1589],{"className":1578},[47],[49,1580],{"disabled":51,"type":52}," It separates every layout-triggering ",[620,1583,1584],{},"read"," into one pass, before any layout-invalidating ",[620,1587,1588],{},"write"," happens; the browser only needs a single reflow to satisfy the whole read pass, and the later write pass never needs to be flushed synchronously because nothing reads from layout afterward",[44,1591,1593,1595,1596,1598,1599,1601],{"className":1592},[47],[49,1594],{"disabled":51,"type":52}," It works because ",[22,1597,66],{}," automatically caches each element's ",[22,1600,1360],{}," the first time it's accessed",[44,1603,1605,1595,1607,1609],{"className":1604},[47],[49,1606],{"disabled":51,"type":52},[22,1608,954],{}," runs asynchronously, giving the browser idle time to repaint between the reads and the writes",[100,1611,1612,1614,1623],{},[103,1613,105],{},[107,1615,1616,1618,1619,1585,1621,1589],{},[110,1617,112],{}," B — It separates every layout-triggering ",[620,1620,1584],{},[620,1622,1588],{},[107,1624,1625,1627,1628,1630,1631,1633,1634,1636,1637,1639,1640,1642,1643,1645],{},[110,1626,126],{}," This is the \"read-then-write\" batching idiom (the core idea behind libraries like FastDOM). Since nothing has invalidated layout yet when the first loop reads ",[22,1629,1360],{},", at most one reflow satisfies the entire batch of reads. The second loop's writes can then all queue up freely, because no subsequent read in this code forces the browser to flush them synchronously. ",[110,1632,1435],{}," one reflow for a batch beats ",[620,1635,1428],{}," reflows for ",[620,1638,1428],{}," elements by a wide margin as the list grows. Option A misses that a single triggered reflow is vastly cheaper than repeating it per element. Options C and D invent mechanisms that don't exist — ",[22,1641,66],{}," doesn't cache layout metrics, and ",[22,1644,849],{}," is fully synchronous.",[14,1647,1648,1652,1755,1807],{"language":153},[17,1649,1651],{"id":1650},"q10-how-should-this-loop-be-rewritten-to-minimize-reflows-when-inserting-1000-items","Q10. How should this loop be rewritten to minimize reflows when inserting 1,000 items?",[163,1653,1654],{"language":153},[166,1655,1657],{"className":168,"code":1656,"language":153,"meta":170,"style":170},"const list = document.querySelector('#list');\nfor (let i = 0; i \u003C 1000; i++) {\n  const li = document.createElement('li');\n  li.textContent = `Item ${i}`;\n  list.appendChild(li);\n}\n",[22,1658,1659,1679,1706,1725,1742,1751],{"__ignoreMap":170},[174,1660,1661,1663,1666,1668,1670,1672,1674,1677],{"class":176,"line":177},[174,1662,181],{"class":180},[174,1664,1665],{"class":184}," list",[174,1667,188],{"class":180},[174,1669,192],{"class":191},[174,1671,132],{"class":195},[174,1673,198],{"class":191},[174,1675,1676],{"class":201},"'#list'",[174,1678,205],{"class":191},[174,1680,1681,1683,1685,1687,1689,1691,1693,1695,1697,1700,1702,1704],{"class":176,"line":208},[174,1682,674],{"class":180},[174,1684,677],{"class":191},[174,1686,680],{"class":180},[174,1688,683],{"class":191},[174,1690,264],{"class":180},[174,1692,688],{"class":184},[174,1694,691],{"class":191},[174,1696,694],{"class":180},[174,1698,1699],{"class":184}," 1000",[174,1701,702],{"class":191},[174,1703,705],{"class":180},[174,1705,708],{"class":191},[174,1707,1708,1711,1713,1715,1717,1719,1721,1723],{"class":176,"line":230},[174,1709,1710],{"class":180},"  const",[174,1712,241],{"class":184},[174,1714,188],{"class":180},[174,1716,192],{"class":191},[174,1718,248],{"class":195},[174,1720,198],{"class":191},[174,1722,253],{"class":201},[174,1724,205],{"class":191},[174,1726,1727,1730,1732,1735,1737,1740],{"class":176,"line":236},[174,1728,1729],{"class":191},"  li.textContent ",[174,1731,264],{"class":180},[174,1733,1734],{"class":201}," `Item ${",[174,1736,803],{"class":191},[174,1738,1739],{"class":201},"}`",[174,1741,270],{"class":191},[174,1743,1744,1747,1749],{"class":176,"line":258},[174,1745,1746],{"class":191},"  list.",[174,1748,289],{"class":195},[174,1750,292],{"class":191},[174,1752,1753],{"class":176,"line":273},[174,1754,727],{"class":191},[39,1756,1758,1775,1784,1797],{"className":1757},[42],[44,1759,1761,1763,1764,1766,1767,1770,1771,1774],{"className":1760},[47],[49,1762],{"disabled":51,"type":52}," Build every ",[22,1765,395],{}," into a ",[22,1768,1769],{},"DocumentFragment"," first, then append the fragment to ",[22,1772,1773],{},"list"," once after the loop — the fragment lives outside the render tree, so populating it triggers no reflows, and only the single final append touches the live DOM",[44,1776,1778,1780,1781,1783],{"className":1777},[47],[49,1779],{"disabled":51,"type":52}," Nothing needs to change — ",[22,1782,289],{}," calls inside a loop are already batched by the browser automatically",[44,1785,1787,1789,1790,1792,1793,1796],{"className":1786},[47],[49,1788],{"disabled":51,"type":52}," Replace ",[22,1791,289],{}," with ",[22,1794,1795],{},"insertAdjacentHTML('beforeend', ...)"," inside the loop, since string-based insertion never triggers layout",[44,1798,1800,1802,1803,1806],{"className":1799},[47],[49,1801],{"disabled":51,"type":52}," Wrap the loop body in ",[22,1804,1805],{},"requestAnimationFrame"," so each insertion happens on its own frame",[100,1808,1809,1811,1822],{},[103,1810,105],{},[107,1812,1813,1815,1816,1766,1818,1770,1820,1774],{},[110,1814,112],{}," A — Build every ",[22,1817,395],{},[22,1819,1769],{},[22,1821,1773],{},[107,1823,1824,1826,1827,1829,1830,1832,1833,1835,1836,1838,1839,1841],{},[110,1825,126],{}," A ",[22,1828,1769],{}," is a lightweight, in-memory container that is never part of the visible document tree, so appending 1,000 children to it costs no layout or paint work at all. Only the single fragment-into-",[22,1831,1773],{}," append potentially triggers a reflow — one, instead of up to 1,000. ",[110,1834,1435],{}," this is the standard batching idiom for bulk DOM insertion. Option B is false: each ",[22,1837,289],{}," call directly onto a connected ",[22,1840,1773],{}," element is a live-DOM mutation, each one a potential invalidation. Option C still mutates the live DOM on every iteration, with the added cost of re-parsing an HTML string each time. Option D would spread 1,000 insertions across 1,000 separate animation frames — far slower and visibly janky, not faster.",[14,1843,1844,1852,1916,1976],{"language":153},[17,1845,1847,1848,1851],{"id":1846},"q11-what-is-the-primary-risk-with-this-function-if-usersuppliedcomment-comes-from-another-users-input-eg-a-public-comment-form-and-whats-the-safer-alternative","Q11. What is the primary risk with this function if ",[22,1849,1850],{},"userSuppliedComment"," comes from another user's input (e.g., a public comment form), and what's the safer alternative?",[163,1853,1854],{"language":153},[166,1855,1857],{"className":168,"code":1856,"language":153,"meta":170,"style":170},"function showComment(rawText) {\n  const el = document.querySelector('#comment');\n  el.innerHTML = rawText;\n}\nshowComment(userSuppliedComment);\n",[22,1858,1859,1874,1894,1904,1908],{"__ignoreMap":170},[174,1860,1861,1864,1867,1869,1872],{"class":176,"line":177},[174,1862,1863],{"class":180},"function",[174,1865,1866],{"class":195}," showComment",[174,1868,198],{"class":191},[174,1870,1871],{"class":1315},"rawText",[174,1873,708],{"class":191},[174,1875,1876,1878,1881,1883,1885,1887,1889,1892],{"class":176,"line":208},[174,1877,1710],{"class":180},[174,1879,1880],{"class":184}," el",[174,1882,188],{"class":180},[174,1884,192],{"class":191},[174,1886,132],{"class":195},[174,1888,198],{"class":191},[174,1890,1891],{"class":201},"'#comment'",[174,1893,205],{"class":191},[174,1895,1896,1899,1901],{"class":176,"line":230},[174,1897,1898],{"class":191},"  el.innerHTML ",[174,1900,264],{"class":180},[174,1902,1903],{"class":191}," rawText;\n",[174,1905,1906],{"class":176,"line":236},[174,1907,727],{"class":191},[174,1909,1910,1913],{"class":176,"line":258},[174,1911,1912],{"class":195},"showComment",[174,1914,1915],{"class":191},"(userSuppliedComment);\n",[39,1917,1919,1932,1944,1961],{"className":1918},[42],[44,1920,1922,63,1924,1927,1928,1931],{"className":1921},[47],[49,1923],{"disabled":51,"type":52},[22,1925,1926],{},"innerHTML"," is merely slower than ",[22,1929,1930],{},"textContent",", so the only real issue here is performance, not correctness",[44,1933,1935,1937,1938,1941,1942],{"className":1934},[47],[49,1936],{"disabled":51,"type":52}," There's no real risk — browsers automatically strip any ",[22,1939,1940],{},"\u003Cscript>"," tags assigned via ",[22,1943,1926],{},[44,1945,1947,1949,1950,1952,1953,1956,1957,1960],{"className":1946},[47],[49,1948],{"disabled":51,"type":52}," Assigning untrusted input to ",[22,1951,1926],{}," parses it as real HTML, so a comment like ",[22,1954,1955],{},"\u003Cimg src=x onerror=\"steal()\">"," executes arbitrary JavaScript in the page (a classic XSS vector); use ",[22,1958,1959],{},"el.textContent = rawText"," instead, which inserts the string as a raw text node with no HTML parsing at all",[44,1962,1964,1966,1967,428,1970,1972,1973,1975],{"className":1963},[47],[49,1965],{"disabled":51,"type":52}," The fix is to use ",[22,1968,1969],{},"innerText",[22,1971,1926],{},", since ",[22,1974,1969],{}," automatically escapes any HTML in the string",[100,1977,1978,1980,1991],{},[103,1979,105],{},[107,1981,1982,1984,1985,1952,1987,1956,1989,1960],{},[110,1983,112],{}," C — Assigning untrusted input to ",[22,1986,1926],{},[22,1988,1955],{},[22,1990,1959],{},[107,1992,1993,63,1995,63,1998,2000,2001,2003,2004,2007,2008,2010,2011,2013,2014,2017,2018,2020],{},[110,1994,126],{},[110,1996,1997],{},"Safety:",[22,1999,1926],{}," hands its string directly to the HTML parser, so any markup or event-handler attribute embedded in untrusted input becomes real, executing HTML\u002FJS — this is exactly how stored and reflected XSS attacks work in practice. ",[22,2002,1930],{}," never parses its argument as markup; the string becomes a literal text node, so ",[22,2005,2006],{},"\u003Cimg ...>"," shows up as visible, inert text rather than an executed tag. Option B is a dangerous misconception — browsers do not sanitize ",[22,2009,1926],{}," input by default; that's entirely the caller's responsibility. Option D is wrong: ",[22,2012,1969],{}," is about ",[620,2015,2016],{},"rendered"," text and layout, not escaping — writing to it has the same \"no HTML parsing\" property as ",[22,2019,1930],{},", but it isn't the idiomatic choice here because of its own layout costs (see Q12).",[14,2022,2023,2037,2088],{},[17,2024,2026,2027,33,2030,2033,2034,2036],{"id":2025},"q12-elementtextcontent-hello-and-elementinnertext-hello-both-set-visible-text-but-why-is-textcontent-generally-preferred-in-performance-sensitive-code","Q12. ",[22,2028,2029],{},"element.textContent = 'Hello'",[22,2031,2032],{},"element.innerText = 'Hello'"," both set visible text, but why is ",[22,2035,1930],{}," generally preferred in performance-sensitive code?",[39,2038,2040,2048,2059,2070],{"className":2039},[42],[44,2041,2043,63,2045,2047],{"className":2042},[47],[49,2044],{"disabled":51,"type":52},[22,2046,1969],{}," cannot hold more than 255 characters without silently truncating",[44,2049,2051,63,2053,2055,2056,2058],{"className":2050},[47],[49,2052],{"disabled":51,"type":52},[22,2054,1930],{}," is deprecated in favor of ",[22,2057,1969],{}," in every modern browser",[44,2060,2062,63,2064,2066,2067,2069],{"className":2061},[47],[49,2063],{"disabled":51,"type":52},[22,2065,1969],{}," parses its argument as HTML while ",[22,2068,1930],{}," does not",[44,2071,2073,63,2075,2077,2078,2080,2081,2084,2085,2087],{"className":2072},[47],[49,2074],{"disabled":51,"type":52},[22,2076,1969],{}," is defined in terms of the ",[620,2079,2016],{}," page — it must account for applied CSS (e.g., skipping ",[22,2082,2083],{},"display: none"," content, normalizing whitespace to match what's visually shown) — so reading or writing it can force a synchronous reflow; ",[22,2086,1930],{}," operates purely on the raw text nodes in the DOM tree and never touches layout",[100,2089,2090,2092,2105],{},[103,2091,105],{},[107,2093,2094,2096,2097,2077,2099,2080,2101,2084,2103,2087],{},[110,2095,112],{}," D — ",[22,2098,1969],{},[620,2100,2016],{},[22,2102,2083],{},[22,2104,1930],{},[107,2106,2107,63,2109,746,2111,2113,2114,2117,2118,2120,2121,2123],{},[110,2108,126],{},[110,2110,1435],{},[22,2112,1969],{}," reflects what a user would actually ",[620,2115,2116],{},"see"," — excluding hidden elements, collapsing whitespace the way rendering would, respecting line breaks introduced by CSS — the engine needs up-to-date layout information to compute or apply it, which can trigger the same kind of synchronous reflow flagged back in Q8. ",[22,2119,1930],{}," simply walks the raw text-node tree, entirely layout-agnostic, making it both faster and more predictable. Option C is backwards — neither property parses HTML; that's ",[22,2122,1926],{},"'s job exclusively. Options A and B are fabricated.",[14,2125,2126,2134,2238,2288],{"language":153},[17,2127,2129,2130,2133],{"id":2128},"q13-what-happens-when-copyclick-runs","Q13. What happens when ",[22,2131,2132],{},"copy.click()"," runs?",[163,2135,2136],{"language":153},[166,2137,2139],{"className":168,"code":2138,"language":153,"meta":170,"style":170},"const original = document.querySelector('#save-btn');\noriginal.addEventListener('click', () => console.log('saved'));\n\nconst copy = original.cloneNode(true);\ndocument.body.appendChild(copy);\ncopy.click();\n",[22,2140,2141,2161,2191,2195,2217,2227],{"__ignoreMap":170},[174,2142,2143,2145,2148,2150,2152,2154,2156,2159],{"class":176,"line":177},[174,2144,181],{"class":180},[174,2146,2147],{"class":184}," original",[174,2149,188],{"class":180},[174,2151,192],{"class":191},[174,2153,132],{"class":195},[174,2155,198],{"class":191},[174,2157,2158],{"class":201},"'#save-btn'",[174,2160,205],{"class":191},[174,2162,2163,2166,2169,2171,2174,2177,2179,2182,2184,2186,2189],{"class":176,"line":208},[174,2164,2165],{"class":191},"original.",[174,2167,2168],{"class":195},"addEventListener",[174,2170,198],{"class":191},[174,2172,2173],{"class":201},"'click'",[174,2175,2176],{"class":191},", () ",[174,2178,1529],{"class":180},[174,2180,2181],{"class":191}," console.",[174,2183,214],{"class":195},[174,2185,198],{"class":191},[174,2187,2188],{"class":201},"'saved'",[174,2190,1067],{"class":191},[174,2192,2193],{"class":176,"line":230},[174,2194,233],{"emptyLinePlaceholder":51},[174,2196,2197,2199,2202,2204,2207,2210,2212,2215],{"class":176,"line":236},[174,2198,181],{"class":180},[174,2200,2201],{"class":184}," copy",[174,2203,188],{"class":180},[174,2205,2206],{"class":191}," original.",[174,2208,2209],{"class":195},"cloneNode",[174,2211,198],{"class":191},[174,2213,2214],{"class":184},"true",[174,2216,205],{"class":191},[174,2218,2219,2222,2224],{"class":176,"line":258},[174,2220,2221],{"class":191},"document.body.",[174,2223,289],{"class":195},[174,2225,2226],{"class":191},"(copy);\n",[174,2228,2229,2232,2235],{"class":176,"line":273},[174,2230,2231],{"class":191},"copy.",[174,2233,2234],{"class":195},"click",[174,2236,2237],{"class":191},"();\n",[39,2239,2241,2254,2269,2280],{"className":2240},[42],[44,2242,2244,2246,2247,322,2250,2253],{"className":2243},[47],[49,2245],{"disabled":51,"type":52}," It logs ",[22,2248,2249],{},"\"saved\"",[22,2251,2252],{},"cloneNode(true)"," performs a deep clone that includes all attached event listeners",[44,2255,2257,2259,2260,2262,2263,2265,2266,2268],{"className":2256},[47],[49,2258],{"disabled":51,"type":52}," Nothing is logged — ",[22,2261,2209],{}," copies the element's tag, attributes, and (with ",[22,2264,2214],{},") its descendant nodes, but it never copies listeners attached via ",[22,2267,2168],{},", since those live outside the node's cloneable attribute\u002Fchild data",[44,2270,2272,742,2274,2276,2277],{"className":2271},[47],[49,2273],{"disabled":51,"type":52},[22,2275,353],{},", because a cloned node cannot dispatch synthetic events like ",[22,2278,2279],{},".click()",[44,2281,2283,2246,2285,2287],{"className":2282},[47],[49,2284],{"disabled":51,"type":52},[22,2286,2249],{}," twice — once for the original element's listener and once for a listener copy on the clone",[100,2289,2290,2292,2303],{},[103,2291,105],{},[107,2293,2294,2296,2297,2262,2299,2265,2301,2268],{},[110,2295,112],{}," B — Nothing is logged — ",[22,2298,2209],{},[22,2300,2214],{},[22,2302,2168],{},[107,2304,2305,63,2307,63,2309,2311,2312,2315,2316,2319,2320,2323,2324,2326,2327,2329,2330,2332],{},[110,2306,126],{},[110,2308,819],{},[22,2310,2252],{}," performs a deep clone, duplicating the tag, every attribute, and all descendant nodes recursively; ",[22,2313,2314],{},"cloneNode(false)"," (or ",[22,2317,2318],{},"cloneNode()"," with no argument) does the same but shallow, omitting children entirely. In neither case does cloning touch the internal listener list an ",[22,2321,2322],{},"EventTarget"," accumulates via ",[22,2325,2168],{}," — that bookkeeping is stored on the live object, not in the serializable DOM structure the clone algorithm copies. Any ",[22,2328,2168],{},"-based behavior has to be reattached to the clone manually. Option A is an extremely common wrong assumption. Options C and D fabricate behavior — ",[22,2331,2279],{}," works fine on any connected element, and there's no listener present on the clone at all to fire twice.",[14,2334,2335,2347,2407],{},[17,2336,2338,2339,2342,2343,2346],{"id":2337},"q14-a-toggle-visibility-helper-is-implemented-two-ways-version-1-elclassname-elclassnameincludeshidden-elclassnamereplacehidden-elclassname-hidden-version-2-elclasslisttogglehidden-why-is-version-2-the-idiomatic-choice","Q14. A toggle-visibility helper is implemented two ways. Version 1: ",[22,2340,2341],{},"el.className = el.className.includes('hidden') ? el.className.replace('hidden', '') : el.className + ' hidden'",". Version 2: ",[22,2344,2345],{},"el.classList.toggle('hidden')",". Why is Version 2 the idiomatic choice?",[39,2348,2350,2371,2383,2394],{"className":2349},[42],[44,2351,2353,63,2355,2358,2359,2362,2363,2366,2367,2370],{"className":2352},[47],[49,2354],{"disabled":51,"type":52},[22,2356,2357],{},"classList.toggle"," handles the add\u002Fremove logic atomically and correctly regardless of existing whitespace or class order, while manual ",[22,2360,2361],{},"className"," string manipulation is error-prone — ",[22,2364,2365],{},".replace('hidden', '')"," is a plain substring match that can corrupt an unrelated class like ",[22,2368,2369],{},"\"overhidden\"",", and repeated concatenation without a duplicate check can pile up the same class many times over",[44,2372,2374,63,2376,2378,2379,2382],{"className":2373},[47],[49,2375],{"disabled":51,"type":52},[22,2377,2361],{}," was removed from the DOM specification in favor of ",[22,2380,2381],{},"classList",", so Version 1 no longer runs in any browser",[44,2384,2386,63,2388,2390,2391,2393],{"className":2385},[47],[49,2387],{"disabled":51,"type":52},[22,2389,2357],{}," is the only one of the two that triggers a repaint; assigning to ",[22,2392,2361],{}," does not update the visible page at all",[44,2395,2397,2399,2400,2403,2404,2406],{"className":2396},[47],[49,2398],{"disabled":51,"type":52}," Version 1 is actually preferable for performance, since it avoids the overhead of the ",[22,2401,2402],{},"DOMTokenList"," object that ",[22,2405,2381],{}," allocates",[100,2408,2409,2411,2423],{},[103,2410,105],{},[107,2412,2413,379,2415,2358,2417,2362,2419,2366,2421,2370],{},[110,2414,112],{},[22,2416,2357],{},[22,2418,2361],{},[22,2420,2365],{},[22,2422,2369],{},[107,2424,2425,63,2427,63,2430,2432,2433,1792,2435,2438,2439,2438,2441,2438,2444,2447,2448,2451,2452,2454,2455,2457,2458,2460,2461,2464,2465,2467],{},[110,2426,126],{},[110,2428,2429],{},"Idiom:",[22,2431,2381],{}," exposes a ",[22,2434,2402],{},[22,2436,2437],{},"add","\u002F",[22,2440,716],{},[22,2442,2443],{},"toggle",[22,2445,2446],{},"contains"," that operate correctly on the space-separated token list — ",[22,2449,2450],{},"toggle('hidden')"," is guaranteed to leave the element in exactly one consistent state, with no malformed whitespace and no duplicate tokens. Direct ",[22,2453,2361],{}," string manipulation is a classic source of subtle bugs: ",[22,2456,2365],{}," performs a bare substring match that can silently mangle an unrelated class name that merely ",[620,2459,2446],{}," \"hidden\" as a substring, and naive concatenation without checking ",[22,2462,2463],{},"includes"," first can accumulate the same class repeatedly if the function runs more than once. Option B is false — ",[22,2466,2361],{}," remains fully valid, just less ergonomic. Options C and D are fabricated technical claims.",[14,2469,2470,2486,2533,2590],{"language":153},[17,2471,2473,2474,2477,2478,2481,2482,2485],{"id":2472},"q15-given-div-iduser-card-data-user-id482-data-is-verifiedtruediv-what-is-the-correct-way-to-read-the-data-user-id-attribute-via-the-dataset-api-and-what-type-comes-back","Q15. Given ",[22,2475,2476],{},"\u003Cdiv id=\"user-card\" data-user-id=\"482\" data-is-verified=\"true\">\u003C\u002Fdiv>",", what is the correct way to read the ",[22,2479,2480],{},"data-user-id"," attribute via the ",[22,2483,2484],{},"dataset"," API, and what type comes back?",[163,2487,2488],{"language":153},[166,2489,2491],{"className":168,"code":2490,"language":153,"meta":170,"style":170},"\u002F\u002F \u003Cdiv id=\"user-card\" data-user-id=\"482\" data-is-verified=\"true\">\u003C\u002Fdiv>\nconst card = document.querySelector('#user-card');\nconsole.log(card.dataset.userId, typeof card.dataset.userId);\n",[22,2492,2493,2498,2518],{"__ignoreMap":170},[174,2494,2495],{"class":176,"line":177},[174,2496,2497],{"class":226},"\u002F\u002F \u003Cdiv id=\"user-card\" data-user-id=\"482\" data-is-verified=\"true\">\u003C\u002Fdiv>\n",[174,2499,2500,2502,2505,2507,2509,2511,2513,2516],{"class":176,"line":208},[174,2501,181],{"class":180},[174,2503,2504],{"class":184}," card",[174,2506,188],{"class":180},[174,2508,192],{"class":191},[174,2510,132],{"class":195},[174,2512,198],{"class":191},[174,2514,2515],{"class":201},"'#user-card'",[174,2517,205],{"class":191},[174,2519,2520,2522,2524,2527,2530],{"class":176,"line":230},[174,2521,211],{"class":191},[174,2523,214],{"class":195},[174,2525,2526],{"class":191},"(card.dataset.userId, ",[174,2528,2529],{"class":180},"typeof",[174,2531,2532],{"class":191}," card.dataset.userId);\n",[39,2534,2536,2548,2559,2580],{"className":2535},[42],[44,2537,2539,63,2541,2544,2545],{"className":2538},[47],[49,2540],{"disabled":51,"type":52},[22,2542,2543],{},"card.dataset['data-user-id']",", and it comes back as a ",[22,2546,2547],{},"Number",[44,2549,2551,63,2553,2544,2556],{"className":2550},[47],[49,2552],{"disabled":51,"type":52},[22,2554,2555],{},"card.dataset.data_user_id",[22,2557,2558],{},"Boolean",[44,2560,2562,63,2564,2544,2567,677,2570,2573,2574,2576,2577],{"className":2561},[47],[49,2563],{"disabled":51,"type":52},[22,2565,2566],{},"card.dataset.userId",[22,2568,2569],{},"String",[22,2571,2572],{},"\"482\"",") — ",[22,2575,2484],{}," always yields strings, so numeric-looking values need explicit conversion, e.g. ",[22,2578,2579],{},"Number(card.dataset.userId)",[44,2581,2583,63,2585,2544,2588],{"className":2582},[47],[49,2584],{"disabled":51,"type":52},[22,2586,2587],{},"card.getAttribute('dataset.userId')",[22,2589,2569],{},[100,2591,2592,2594,2608],{},[103,2593,105],{},[107,2595,2596,594,2598,2544,2600,677,2602,2573,2604,2576,2606],{},[110,2597,112],{},[22,2599,2566],{},[22,2601,2569],{},[22,2603,2572],{},[22,2605,2484],{},[22,2607,2579],{},[107,2609,2610,2612,2613,2615,2616,2619,2620,2622,2623,143,2626,2622,2629,2632,2633,2635,2636,88,2639,2635,2642,2644,2645,2648,2649,2652,2653,2655,2656,2659],{},[110,2611,126],{}," The ",[22,2614,2484],{}," API automatically converts a hyphen-separated ",[22,2617,2618],{},"data-*"," attribute name into camelCase for property access: ",[22,2621,2480],{}," becomes ",[22,2624,2625],{},"dataset.userId",[22,2627,2628],{},"data-is-verified",[22,2630,2631],{},"dataset.isVerified",". Every value comes back as a plain string regardless of how it looks — ",[22,2634,2572],{},", not ",[22,2637,2638],{},"482",[22,2640,2641],{},"\"true\"",[22,2643,2214],{}," — so callers must explicitly coerce with ",[22,2646,2647],{},"Number(...)"," or compare ",[22,2650,2651],{},"=== 'true'"," as needed. Option A uses the wrong key format (it should be camelCase, not the literal hyphenated attribute name as a bracket key) and the wrong type assumption. Option B uses invalid snake_case syntax that the conversion rule doesn't produce. Option D conflates ",[22,2654,2484],{}," (a property) with ",[22,2657,2658],{},"getAttribute"," (a method that takes the literal attribute name, not a dataset key path).",[14,2661,2662,2670,2717,2759],{"language":153},[17,2663,2665,2666,2669],{"id":2664},"q16-what-does-elouterhtml-show-after-this-assignment","Q16. What does ",[22,2667,2668],{},"el.outerHTML"," show after this assignment?",[163,2671,2672],{"language":153},[166,2673,2675],{"className":168,"code":2674,"language":153,"meta":170,"style":170},"const el = document.createElement('div');\nel.dataset.orderStatus = 'pending';\nconsole.log(el.outerHTML);\n",[22,2676,2677,2696,2708],{"__ignoreMap":170},[174,2678,2679,2681,2683,2685,2687,2689,2691,2694],{"class":176,"line":177},[174,2680,181],{"class":180},[174,2682,1880],{"class":184},[174,2684,188],{"class":180},[174,2686,192],{"class":191},[174,2688,248],{"class":195},[174,2690,198],{"class":191},[174,2692,2693],{"class":201},"'div'",[174,2695,205],{"class":191},[174,2697,2698,2701,2703,2706],{"class":176,"line":208},[174,2699,2700],{"class":191},"el.dataset.orderStatus ",[174,2702,264],{"class":180},[174,2704,2705],{"class":201}," 'pending'",[174,2707,270],{"class":191},[174,2709,2710,2712,2714],{"class":176,"line":230},[174,2711,211],{"class":191},[174,2713,214],{"class":195},[174,2715,2716],{"class":191},"(el.outerHTML);\n",[39,2718,2720,2728,2736,2744],{"className":2719},[42],[44,2721,2723,63,2725],{"className":2722},[47],[49,2724],{"disabled":51,"type":52},[22,2726,2727],{},"\u003Cdiv dataset-order-status=\"pending\">\u003C\u002Fdiv>",[44,2729,2731,63,2733],{"className":2730},[47],[49,2732],{"disabled":51,"type":52},[22,2734,2735],{},"\u003Cdiv data-orderStatus=\"pending\">\u003C\u002Fdiv>",[44,2737,2739,63,2741],{"className":2738},[47],[49,2740],{"disabled":51,"type":52},[22,2742,2743],{},"\u003Cdiv orderStatus=\"pending\">\u003C\u002Fdiv>",[44,2745,2747,63,2749,2752,2753,2755,2756,2758],{"className":2746},[47],[49,2748],{"disabled":51,"type":52},[22,2750,2751],{},"\u003Cdiv data-order-status=\"pending\">\u003C\u002Fdiv>"," — writing to a camelCase ",[22,2754,2484],{}," property converts it back into a hyphen-separated ",[22,2757,2618],{}," attribute on the element",[100,2760,2761,2763,2773],{},[103,2762,105],{},[107,2764,2765,2096,2767,2752,2769,2755,2771,2758],{},[110,2766,112],{},[22,2768,2751],{},[22,2770,2484],{},[22,2772,2618],{},[107,2774,2775,2612,2777,2779,2780,2783,2784,2787,2788,2790,2791,2793,2794,2796,2797,2800,2801,2803],{},[110,2776,126],{},[22,2778,2484],{}," conversion (introduced in Q15) is symmetric: reading turns ",[22,2781,2782],{},"data-order-status"," into ",[22,2785,2786],{},"dataset.orderStatus",", and writing to ",[22,2789,2786],{}," creates or updates the ",[22,2792,2782],{}," attribute, inserting a hyphen before each formerly-uppercase letter and lowercasing it. Option A wrongly assumes the word ",[22,2795,2484],{}," itself becomes part of the attribute name. Option B fails to convert the camelCase key back into kebab-case. Option C drops the required ",[22,2798,2799],{},"data-"," prefix entirely, which would just create a non-standard, untracked custom attribute that ",[22,2802,2484],{}," would never pick back up.",[14,2805,2806,2818,2982,3028],{"language":153},[17,2807,2809,2810,2813,2814,2817],{"id":2808},"q17-if-removerowrow-is-called-to-remove-a-row-from-the-visible-table-but-cachedrows-still-holds-a-reference-to-it-what-happens","Q17. If ",[22,2811,2812],{},"removeRow(row)"," is called to remove a row from the visible table, but ",[22,2815,2816],{},"cachedRows"," still holds a reference to it, what happens?",[163,2819,2820],{"language":153},[166,2821,2823],{"className":168,"code":2822,"language":153,"meta":170,"style":170},"let cachedRows = [];\n\nfunction removeRow(row) {\n  row.remove();\n}\n\nfunction renderRow(data) {\n  const row = document.createElement('tr');\n  row.textContent = data.label;\n  row.addEventListener('click', () => console.log(data.id));\n  cachedRows.push(row);\n  document.querySelector('tbody').appendChild(row);\n  return row;\n}\n",[22,2824,2825,2836,2840,2854,2863,2867,2871,2885,2905,2916,2938,2949,2968,2977],{"__ignoreMap":170},[174,2826,2827,2829,2832,2834],{"class":176,"line":177},[174,2828,680],{"class":180},[174,2830,2831],{"class":191}," cachedRows ",[174,2833,264],{"class":180},[174,2835,1483],{"class":191},[174,2837,2838],{"class":176,"line":208},[174,2839,233],{"emptyLinePlaceholder":51},[174,2841,2842,2844,2847,2849,2852],{"class":176,"line":230},[174,2843,1863],{"class":180},[174,2845,2846],{"class":195}," removeRow",[174,2848,198],{"class":191},[174,2850,2851],{"class":1315},"row",[174,2853,708],{"class":191},[174,2855,2856,2859,2861],{"class":176,"line":236},[174,2857,2858],{"class":191},"  row.",[174,2860,716],{"class":195},[174,2862,2237],{"class":191},[174,2864,2865],{"class":176,"line":258},[174,2866,727],{"class":191},[174,2868,2869],{"class":176,"line":273},[174,2870,233],{"emptyLinePlaceholder":51},[174,2872,2873,2875,2878,2880,2883],{"class":176,"line":295},[174,2874,1863],{"class":180},[174,2876,2877],{"class":195}," renderRow",[174,2879,198],{"class":191},[174,2881,2882],{"class":1315},"data",[174,2884,708],{"class":191},[174,2886,2887,2889,2892,2894,2896,2898,2900,2903],{"class":176,"line":300},[174,2888,1710],{"class":180},[174,2890,2891],{"class":184}," row",[174,2893,188],{"class":180},[174,2895,192],{"class":191},[174,2897,248],{"class":195},[174,2899,198],{"class":191},[174,2901,2902],{"class":201},"'tr'",[174,2904,205],{"class":191},[174,2906,2908,2911,2913],{"class":176,"line":2907},9,[174,2909,2910],{"class":191},"  row.textContent ",[174,2912,264],{"class":180},[174,2914,2915],{"class":191}," data.label;\n",[174,2917,2919,2921,2923,2925,2927,2929,2931,2933,2935],{"class":176,"line":2918},10,[174,2920,2858],{"class":191},[174,2922,2168],{"class":195},[174,2924,198],{"class":191},[174,2926,2173],{"class":201},[174,2928,2176],{"class":191},[174,2930,1529],{"class":180},[174,2932,2181],{"class":191},[174,2934,214],{"class":195},[174,2936,2937],{"class":191},"(data.id));\n",[174,2939,2941,2944,2946],{"class":176,"line":2940},11,[174,2942,2943],{"class":191},"  cachedRows.",[174,2945,1501],{"class":195},[174,2947,2948],{"class":191},"(row);\n",[174,2950,2952,2955,2957,2959,2962,2964,2966],{"class":176,"line":2951},12,[174,2953,2954],{"class":191},"  document.",[174,2956,132],{"class":195},[174,2958,198],{"class":191},[174,2960,2961],{"class":201},"'tbody'",[174,2963,286],{"class":191},[174,2965,289],{"class":195},[174,2967,2948],{"class":191},[174,2969,2971,2974],{"class":176,"line":2970},13,[174,2972,2973],{"class":180},"  return",[174,2975,2976],{"class":191}," row;\n",[174,2978,2980],{"class":176,"line":2979},14,[174,2981,727],{"class":191},[39,2983,2985,2997,3009,3018],{"className":2984},[42],[44,2986,2988,2990,2991,2993,2994,2996],{"className":2987},[47],[49,2989],{"disabled":51,"type":52}," The browser automatically clears ",[22,2992,2851],{}," out of ",[22,2995,2816],{}," once it's removed from the DOM, since detached nodes are garbage-collected immediately",[44,2998,3000,3002,3003,3005,3006,3008],{"className":2999},[47],[49,3001],{"disabled":51,"type":52}," The row becomes a \"detached\" DOM node — no longer part of the visible document tree, but still reachable through the ",[22,3004,2816],{}," array — so it, its closure over ",[22,3007,2882],{},", and its click listener can never be garbage-collected; repeating this pattern for many rows leaks memory",[44,3010,3012,63,3014,3017],{"className":3011},[47],[49,3013],{"disabled":51,"type":52},[22,3015,3016],{},"row.remove()"," throws an error if any other references to that node still exist elsewhere in the program",[44,3019,3021,3023,3024,3027],{"className":3020},[47],[49,3022],{"disabled":51,"type":52}," The click listener is automatically stripped by ",[22,3025,3026],{},".remove()",", so only the empty node itself leaks, which is negligible",[100,3029,3030,3032,3041],{},[103,3031,105],{},[107,3033,3034,3036,3037,3005,3039,3008],{},[110,3035,112],{}," B — The row becomes a \"detached\" DOM node — no longer part of the visible document tree, but still reachable through the ",[22,3038,2816],{},[22,3040,2882],{},[107,3042,3043,63,3045,63,3047,3049,3050,3053,3054,3056,3057,3059,3060,3062],{},[110,3044,126],{},[110,3046,1435],{},[22,3048,3026],{}," only detaches a node from its parent in the render tree — it does nothing to any ",[620,3051,3052],{},"other"," JavaScript reference pointing at that same node object. As long as something (an array, a closure, an event registry) still holds it, the garbage collector must keep the entire reachable object graph alive, including the ",[22,3055,2168],{}," closure capturing ",[22,3058,2882],{},". This is a genuinely common leak pattern in single-page apps that cache row\u002Fitem references without cleaning them up alongside DOM removal. Options A and C invent automatic cleanup that doesn't exist — the GC only reclaims objects that are truly unreachable. Option D wrongly assumes ",[22,3061,3026],{}," also strips listeners; it doesn't — they persist on the detached node and would even still fire if the node were ever re-appended.",[14,3064,3065,3080,3128],{},[17,3066,3068,3069,3072,3073,3076,3077,37],{"id":3067},"q18-when-inserting-new-ui-content-that-includes-some-user-controlled-text-eg-a-username-whats-the-key-tradeoff-between-documentcreateelement-appendchild-versus-elementinsertadjacenthtmlbeforeend-markup","Q18. When inserting new UI content that includes some user-controlled text (e.g., a username), what's the key tradeoff between ",[22,3070,3071],{},"document.createElement(...)"," + ",[22,3074,3075],{},"appendChild(...)"," versus ",[22,3078,3079],{},"element.insertAdjacentHTML('beforeend', markup)",[39,3081,3083,3100,3109,3120],{"className":3082},[42],[44,3084,3086,63,3088,2438,3090,3092,3093,3095,3096,3099],{"className":3085},[47],[49,3087],{"disabled":51,"type":52},[22,3089,248],{},[22,3091,289],{}," requires more code but is inherently safe for user-controlled values when the dynamic part is assigned via ",[22,3094,1930],{}," (no HTML parsing occurs); ",[22,3097,3098],{},"insertAdjacentHTML"," is more concise, but it parses its argument as raw HTML, so any user-controlled value interpolated into that string must be escaped manually or it becomes an XSS vector",[44,3101,3103,3105,3106,3108],{"className":3102},[47],[49,3104],{"disabled":51,"type":52}," Both approaches are equally safe by default — the only real difference is that ",[22,3107,3098],{}," is slightly slower due to extra function-call overhead",[44,3110,3112,63,3114,3116,3117,3119],{"className":3111},[47],[49,3113],{"disabled":51,"type":52},[22,3115,248],{}," cannot set visible text at all, only attributes, so ",[22,3118,3098],{}," is required whenever text content is involved",[44,3121,3123,63,3125,3127],{"className":3122},[47],[49,3124],{"disabled":51,"type":52},[22,3126,3098],{}," is always the safer choice, because the browser automatically escapes any interpolated template values before parsing the markup",[100,3129,3130,3132,3144],{},[103,3131,105],{},[107,3133,3134,379,3136,2438,3138,3092,3140,3095,3142,3099],{},[110,3135,112],{},[22,3137,248],{},[22,3139,289],{},[22,3141,1930],{},[22,3143,3098],{},[107,3145,3146,3148,3149,3151,3152,3154,3155,3158,3159,3162,3163,3166,3167,3169,3170,3172,3173,3175,3176,3178,3179,2315,3182,3185],{},[110,3147,126],{}," Building nodes programmatically and assigning the dynamic part through ",[22,3150,1930],{}," keeps user data inert by construction — there's no parsing step where markup could ever be interpreted as tags. ",[22,3153,3098],{}," hands its string straight to the HTML parser, so a template like ",[22,3156,3157],{},"`\u003Cli>${username}\u003C\u002Fli>`"," lets a ",[22,3160,3161],{},"username"," containing ",[22,3164,3165],{},"\u003Cimg src=x onerror=...>"," execute exactly like the ",[22,3168,1926],{}," case in Q11. The real tradeoff: ",[22,3171,248],{}," is verbose (multiple statements per element), while ",[22,3174,3098],{}," is compact but pushes all escaping responsibility onto the caller. Options B and D understate or misstate the risk — neither API escapes anything automatically. Option C is simply false; elements built with ",[22,3177,248],{}," set visible text via ",[22,3180,3181],{},".textContent",[22,3183,3184],{},".innerText",") without issue.",[14,3187,3188,3200,3264,3315],{"language":153},[17,3189,3191,3192,3195,3196,3199],{"id":3190},"q19-given-input-idemail-typetext-valuedefaultexamplecom-after-the-user-clears-the-field-and-types-newexamplecom-what-do-the-two-logs-print","Q19. Given ",[22,3193,3194],{},"\u003Cinput id=\"email\" type=\"text\" value=\"default@example.com\">",", after the user clears the field and types ",[22,3197,3198],{},"\"new@example.com\"",", what do the two logs print?",[163,3201,3202],{"language":153},[166,3203,3205],{"className":168,"code":3204,"language":153,"meta":170,"style":170},"\u002F\u002F \u003Cinput id=\"email\" type=\"text\" value=\"default@example.com\">\nconst input = document.querySelector('#email');\n\u002F\u002F user clears the field and types \"new@example.com\"\nconsole.log(input.value);\nconsole.log(input.getAttribute('value'));\n",[22,3206,3207,3212,3232,3237,3246],{"__ignoreMap":170},[174,3208,3209],{"class":176,"line":177},[174,3210,3211],{"class":226},"\u002F\u002F \u003Cinput id=\"email\" type=\"text\" value=\"default@example.com\">\n",[174,3213,3214,3216,3219,3221,3223,3225,3227,3230],{"class":176,"line":208},[174,3215,181],{"class":180},[174,3217,3218],{"class":184}," input",[174,3220,188],{"class":180},[174,3222,192],{"class":191},[174,3224,132],{"class":195},[174,3226,198],{"class":191},[174,3228,3229],{"class":201},"'#email'",[174,3231,205],{"class":191},[174,3233,3234],{"class":176,"line":230},[174,3235,3236],{"class":226},"\u002F\u002F user clears the field and types \"new@example.com\"\n",[174,3238,3239,3241,3243],{"class":176,"line":236},[174,3240,211],{"class":191},[174,3242,214],{"class":195},[174,3244,3245],{"class":191},"(input.value);\n",[174,3247,3248,3250,3252,3255,3257,3259,3262],{"class":176,"line":258},[174,3249,211],{"class":191},[174,3251,214],{"class":195},[174,3253,3254],{"class":191},"(input.",[174,3256,2658],{"class":195},[174,3258,198],{"class":191},[174,3260,3261],{"class":201},"'value'",[174,3263,1067],{"class":191},[39,3265,3267,3276,3285,3303],{"className":3266},[42],[44,3268,3270,3272,3273,3275],{"className":3269},[47],[49,3271],{"disabled":51,"type":52}," Both print ",[22,3274,3198],{},", since the property and the attribute are always kept in sync",[44,3277,3279,3272,3281,3284],{"className":3278},[47],[49,3280],{"disabled":51,"type":52},[22,3282,3283],{},"\"default@example.com\"",", since neither updates from user interaction, only from explicit JS assignment",[44,3286,3288,63,3290,3293,3294,3296,3297,3293,3300,3302],{"className":3287},[47],[49,3289],{"disabled":51,"type":52},[22,3291,3292],{},"input.value"," prints ",[22,3295,3198],{}," — the live property reflecting what the user actually typed; ",[22,3298,3299],{},"input.getAttribute('value')",[22,3301,3283],{}," — the original HTML attribute, which reflects only the initial\u002Fdefault value and does not track live user input",[44,3304,3306,63,3308,3310,3311,3314],{"className":3305},[47],[49,3307],{"disabled":51,"type":52},[22,3309,3292],{}," throws, because reading the property after user interaction requires calling ",[22,3312,3313],{},"input.reportValidity()"," first",[100,3316,3317,3319,3331],{},[103,3318,105],{},[107,3320,3321,594,3323,3293,3325,3296,3327,3293,3329,3302],{},[110,3322,112],{},[22,3324,3292],{},[22,3326,3198],{},[22,3328,3299],{},[22,3330,3283],{},[107,3332,3333,63,3335,3337,3338,63,3341,3344,3345,3348,3349,3352,3353,63,3355,3358,3359,3362,3363,139,3365,3368,3369,3371],{},[110,3334,126],{},[110,3336,819],{}," for form controls, the ",[22,3339,3340],{},"value",[620,3342,3343],{},"attribute"," (what's literally written in the markup, and what ",[22,3346,3347],{},"getAttribute('value')"," reads) represents only the initial\u002Fdefault value — it stays frozen at the page's original state unless code explicitly calls ",[22,3350,3351],{},"setAttribute",". The ",[22,3354,3340],{},[620,3356,3357],{},"property"," is live and tracks the control's current state continuously as the user types. Most reflected attributes (like ",[22,3360,3361],{},"id",") stay perfectly in sync with their property, which is exactly why this divergence for a handful of stateful form properties (",[22,3364,3340],{},[22,3366,3367],{},"checked",") catches people off guard — a common bug is resetting a form by reading ",[22,3370,3347],{}," and expecting it to reflect what's currently on screen. Options A and B both wrongly assume lockstep behavior; option D fabricates an error that doesn't exist.",[14,3373,3374,3390,3429],{},[17,3375,3377,3378,3381,3382,3385,3386,3389],{"id":3376},"q20-a-widget-needs-to-react-whenever-nodes-are-added-to-a-specific-container-without-knowing-in-advance-which-code-will-trigger-the-change-why-is-mutationobserver-preferred-over-the-older-mutation-events-eg-domnodeinserted-or-manual-setinterval-polling","Q20. A widget needs to react whenever nodes are added to a specific container, without knowing in advance which code will trigger the change. Why is ",[22,3379,3380],{},"MutationObserver"," preferred over the older Mutation Events (e.g., ",[22,3383,3384],{},"DOMNodeInserted",") or manual ",[22,3387,3388],{},"setInterval"," polling?",[39,3391,3393,3401,3410,3419],{"className":3392},[42],[44,3394,3396,63,3398,3400],{"className":3395},[47],[49,3397],{"disabled":51,"type":52},[22,3399,3380],{}," runs synchronously on every single DOM mutation, giving instant, per-change callbacks",[44,3402,3404,3406,3407,3409],{"className":3403},[47],[49,3405],{"disabled":51,"type":52}," Manual polling with ",[22,3408,3388],{}," is actually more efficient, since it avoids the overhead of registering an observer at all",[44,3411,3413,3415,3416,3418],{"className":3412},[47],[49,3414],{"disabled":51,"type":52}," Mutation Events are still the recommended approach in every modern browser; ",[22,3417,3380],{}," was an experimental API that never actually shipped",[44,3420,3422,63,3424,3426,3427],{"className":3421},[47],[49,3423],{"disabled":51,"type":52},[22,3425,3380],{}," delivers batched, asynchronous notifications — queued as a microtask after the relevant DOM changes finish — instead of firing synchronously for every individual mutation the way the deprecated Mutation Events did; this avoids the severe performance cost that got Mutation Events deprecated, while still being far more efficient and timely than polling with ",[22,3428,3388],{},[100,3430,3431,3433,3441],{},[103,3432,105],{},[107,3434,3435,2096,3437,3426,3439],{},[110,3436,112],{},[22,3438,3380],{},[22,3440,3388],{},[107,3442,3443,63,3445,3447,3448,3451,3452,3454,3455,3457],{},[110,3444,126],{},[110,3446,1435],{}," the old Mutation Events fired a synchronous DOM event for ",[620,3449,3450],{},"every single mutation",", which could itself trigger more mutations and more events, cascading into severe performance degradation on real pages — this is precisely why browsers deprecated them. ",[22,3453,3380],{}," instead collects mutations into a batch and delivers them together in one callback as a microtask, which is both cheaper (no per-change event-dispatch overhead, changes get coalesced) and safer (no synchronous reentrancy hazard). It also strictly beats ",[22,3456,3388],{}," polling, which either misses fast changes (interval too long) or burns CPU on redundant checks (interval too short) — the observer only fires when something actually changed. Option A misdescribes it as synchronous per-mutation, which is the old, deprecated behavior it was designed to replace. Options B and C are simply false.",[3459,3460,3461],"style",{},"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 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 .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}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 .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}",{"title":170,"searchDepth":208,"depth":208,"links":3463},[3464,3466,3468,3470,3472,3474,3476,3477,3478,3479,3480,3482,3484,3486,3488,3490,3492,3494,3496,3498],{"id":19,"depth":230,"text":3465},"Q1. A page has multiple \u003Cli class=\"item\"> elements inside a \u003Cul>. What is the key structural difference between the results of document.querySelectorAll('.item') and document.getElementsByClassName('item')?",{"id":156,"depth":230,"text":3467},"Q2. What does the second console.log print?",{"id":424,"depth":230,"text":3469},"Q3. Using getElementsByClassName instead of querySelectorAll for the same scenario, what does the second console.log print?",{"id":638,"depth":230,"text":3471},"Q4. Given 4 elements with class \"flagged\", what actually happens when this loop runs?",{"id":829,"depth":230,"text":3473},"Q5. A developer writes document.getElementsByTagName('img').forEach(img => img.loading = 'lazy') and gets TypeError: items.forEach is not a function. Why, and what's the correct fix?",{"id":981,"depth":230,"text":3475},"Q6. What does the second console.log print, and why does it complicate the \"NodeList = static\" takeaway from Q2?",{"id":1198,"depth":230,"text":1199},{"id":1275,"depth":230,"text":1276},{"id":1441,"depth":230,"text":1442},{"id":1650,"depth":230,"text":1651},{"id":1846,"depth":230,"text":3481},"Q11. What is the primary risk with this function if userSuppliedComment comes from another user's input (e.g., a public comment form), and what's the safer alternative?",{"id":2025,"depth":230,"text":3483},"Q12. element.textContent = 'Hello' and element.innerText = 'Hello' both set visible text, but why is textContent generally preferred in performance-sensitive code?",{"id":2128,"depth":230,"text":3485},"Q13. What happens when copy.click() runs?",{"id":2337,"depth":230,"text":3487},"Q14. A toggle-visibility helper is implemented two ways. Version 1: el.className = el.className.includes('hidden') ? el.className.replace('hidden', '') : el.className + ' hidden'. Version 2: el.classList.toggle('hidden'). Why is Version 2 the idiomatic choice?",{"id":2472,"depth":230,"text":3489},"Q15. Given \u003Cdiv id=\"user-card\" data-user-id=\"482\" data-is-verified=\"true\">\u003C\u002Fdiv>, what is the correct way to read the data-user-id attribute via the dataset API, and what type comes back?",{"id":2664,"depth":230,"text":3491},"Q16. What does el.outerHTML show after this assignment?",{"id":2808,"depth":230,"text":3493},"Q17. If removeRow(row) is called to remove a row from the visible table, but cachedRows still holds a reference to it, what happens?",{"id":3067,"depth":230,"text":3495},"Q18. When inserting new UI content that includes some user-controlled text (e.g., a username), what's the key tradeoff between document.createElement(...) + appendChild(...) versus element.insertAdjacentHTML('beforeend', markup)?",{"id":3190,"depth":230,"text":3497},"Q19. Given \u003Cinput id=\"email\" type=\"text\" value=\"default@example.com\">, after the user clears the field and types \"new@example.com\", what do the two logs print?",{"id":3376,"depth":230,"text":3499},"Q20. A widget needs to react whenever nodes are added to a specific container, without knowing in advance which code will trigger the change. Why is MutationObserver preferred over the older Mutation Events (e.g., DOMNodeInserted) or manual setInterval polling?","md",{},"\u002Fjs\u002F19-the-dom",{"title":5,"description":170},"js\u002F19-the-dom","2BjA9eyJrp2ERHO4Zjr033HyoLZ8z-pxfMuDdN_t7Fw",1787335397335]