Four review scenarios. Toggle between the problematic code and the improved version.
Performance
O(n²) DOM manipulation
constitems=document.querySelectorAll('.item');constupdates=getUpdates();// array of items to color// O(n²): nested loop — performance problemfor (leti=0;i<updates.length;i++) {items.forEach(el=>{el.style.color='red';// triggers reflow per itemel.style.fontWeight='bold';el.style.background='#fee';});}
/* CSS: define the state once */.item--highlighted{color:red;font-weight:bold;background:#fee;}// JS: single pass, no nested loopconstitems=document.querySelectorAll('.item');items.forEach(el=>{el.classList.add('item--highlighted');// O(n), no reflow});
Inline style changes in a nested loop force the browser to recalculate layout on every iteration — O(n²) reflows. The fix moves all style declarations into a CSS class and uses a single classList.add() pass. The browser batches the class update into one reflow.
Accessibility
Non-interactive trigger element
<!-- Not keyboard accessible --><divclass="modal-trigger"onclick="openModal()">
Open
</div><!-- Issues:
- div is not focusable by keyboard
- no role conveyed to screen readers
- no indication it opens a dialog
-->
<!-- Keyboard accessible, screen reader friendly --><buttontype="button"aria-haspopup="dialog"aria-controls="modal"onclick="openModal()">
Open
</button><!-- Fixed:
- button is focusable and activatable with Enter/Space
- aria-haspopup="dialog" announces what opens
- aria-controls links to the modal element
-->
A <div> with onclick is invisible to keyboard navigation and screen readers. Replacing it with <button> makes it focusable, activatable with Enter and Space, and understood by assistive technology. The aria-haspopup and aria-controls attributes tell screen readers exactly what the button does.
// Single function, data-driven configconstTOAST_TYPES={success:'toast--success',error:'toast--error',warning:'toast--warning',info:'toast--info',};functionshowToast(msg,type='info') {constel=document.createElement('div');el.className=`toast ${TOAST_TYPES[type]}`;el.textContent=msg;document.body.append(el);}// UsageshowToast('Saved!','success');showToast('Failed to save','error');
Five near-identical functions mean any bug fix or feature change must be applied five times. The refactored version uses one function with a type parameter and a config object. Adding a new toast type is now one line in the config — not a new copy-pasted function.
Security
XSS via innerHTML
// Vulnerable: userInput can contain script tagsconstuserInput=getInputFromURL();// XSS: if userInput = "<img src=x onerror=alert(1)>"// the browser will execute the event handlercommentEl.innerHTML=userInput;// Attacker can steal cookies, redirect users,// or make requests on their behalf.
// Option A: textContent (safest — treats input as plain text)commentEl.textContent=userInput;// Option B: DOMParser sanitization (if you need to allow some HTML)functionsanitize(html) {constdoc=newDOMParser().parseFromString(html,'text/html');doc.querySelectorAll('script, [onclick], [onerror]')
.forEach(el=>el.remove());returndoc.body.innerHTML;}commentEl.innerHTML=sanitize(userInput);
Setting innerHTML with untrusted user input allows Cross-Site Scripting (XSS). An attacker can inject event handlers or script tags that execute in the victim's browser. The safest fix is textContent, which renders the value as literal text. If you need to allow some HTML, parse and strip dangerous nodes before inserting.