Interactive and Dynamic Elements

1. Button Elements and Button Types

Type Behavior Use Case Default
submit Submits parent form Form submission Yes (in forms)
button No default action Custom JavaScript actions No
reset Resets form to defaults Clear form (use sparingly) No

Button vs Input Button

Element Pros Cons
<button> Can contain HTML, more flexible styling Different default in old IE
<input type="button"> Simpler, consistent behavior Text only, less flexible

Button Attributes

Attribute Purpose
type submit, button, reset
disabled Disable button
name Form field name
value Submitted value
form Associate with form by id
formaction Override form action
formmethod Override form method
formnovalidate Skip validation

Example: Button types and usage

<!-- Submit button (default in forms) -->
<form action="/submit">
  <button type="submit">Submit</button>
</form>

<!-- Explicit button type (no form submission) -->
<button type="button" onclick="alert('Clicked!')">Click Me</button>

<!-- Reset button (clears form) -->
<form>
  <input type="text" name="name">
  <button type="reset">Clear Form</button>
</form>

<!-- Button with HTML content -->
<button type="button">
  <svg width="16" height="16">
    <circle cx="8" cy="8" r="7" fill="green"/>
  </svg>
  Save Draft
</button>

<!-- Disabled button -->
<button type="submit" disabled>Processing...</button>

<!-- Button with value (for multiple submits) -->
<form method="POST">
  <button type="submit" name="action" value="save">Save</button>
  <button type="submit" name="action" value="publish">Publish</button>
  <button type="submit" name="action" value="delete" formnovalidate>Delete</button>
</form>

<!-- Button outside form (using form attribute) -->
<form id="myForm">
  <input type="text" name="username">
</form>
<button type="submit" form="myForm">Submit from Outside</button>

<!-- Override form attributes -->
<form action="/default" method="POST">
  <button type="submit">Normal Submit</button>
  <button type="submit" 
          formaction="/alternative" 
          formmethod="GET">
    Alternative Submit
  </button>
</form>

<!-- Styled buttons with states -->
<style>
  button {
    padding: 10px 20px;
    font-size: 16px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s;
  }
  
  button[type="submit"] {
    background-color: #4caf50;
    color: white;
  }
  
  button[type="submit"]:hover {
    background-color: #45a049;
  }
  
  button:disabled {
    background-color: #ccc;
    cursor: not-allowed;
    opacity: 0.6;
  }
  
  button:focus {
    outline: 2px solid #2196f3;
    outline-offset: 2px;
  }
</style>
Warning: Always specify type attribute on buttons. Default is type="submit" inside forms, which can cause unexpected submissions. Use type="button" for JavaScript-only buttons.

2. Details and Summary Disclosure Widgets

Element Purpose Required Behavior
<details> Disclosure widget container Yes Expandable/collapsible content
<summary> Visible heading/toggle Optional (defaults to "Details") Clickable toggle control

Details Attributes

Attribute Purpose
open Initially expanded state
name Accordion group (exclusive open)

Events

Event When Fired
toggle When open state changes

Example: Details and summary widgets

<!-- Basic details/summary -->
<details>
  <summary>Click to expand</summary>
  <p>This content is hidden until the user clicks the summary.</p>
</details>

<!-- Initially open -->
<details open>
  <summary>Already Expanded</summary>
  <p>This content is visible by default.</p>
</details>

<!-- FAQ accordion -->
<details>
  <summary>What is HTML?</summary>
  <p>HTML (HyperText Markup Language) is the standard markup language for creating web pages.</p>
</details>

<details>
  <summary>What is CSS?</summary>
  <p>CSS (Cascading Style Sheets) is used to style HTML elements.</p>
</details>

<details>
  <summary>What is JavaScript?</summary>
  <p>JavaScript is a programming language that adds interactivity to web pages.</p>
</details>

<!-- Exclusive accordion (only one open at a time) -->
<details name="accordion">
  <summary>Section 1</summary>
  <p>Content for section 1.</p>
</details>

<details name="accordion">
  <summary>Section 2</summary>
  <p>Content for section 2. Opening this closes Section 1.</p>
</details>

<details name="accordion">
  <summary>Section 3</summary>
  <p>Content for section 3.</p>
</details>

<!-- Nested details -->
<details>
  <summary>Parent Level</summary>
  <p>Parent content.</p>
  
  <details>
    <summary>Child Level</summary>
    <p>Nested content.</p>
  </details>
</details>

<!-- With complex content -->
<details>
  <summary>View Product Details</summary>
  <table>
    <tr>
      <th>Specification</th>
      <th>Value</th>
    </tr>
    <tr>
      <td>Weight</td>
      <td>1.5 kg</td>
    </tr>
    <tr>
      <td>Dimensions</td>
      <td>30 x 20 x 10 cm</td>
    </tr>
  </table>
</details>

<!-- JavaScript event handling -->
<details id="myDetails">
  <summary>Track Toggle Events</summary>
  <p>Content here.</p>
</details>

<script>
  const details = document.getElementById('myDetails');
  
  details.addEventListener('toggle', (e) => {
    if (details.open) {
      console.log('Details expanded');
    } else {
      console.log('Details collapsed');
    }
  });
</script>

<!-- Styled details/summary -->
<style>
  details {
    border: 1px solid #ddd;
    border-radius: 4px;
    padding: 10px;
    margin: 10px 0;
  }
  
  summary {
    font-weight: bold;
    cursor: pointer;
    padding: 5px;
    user-select: none;
  }
  
  summary:hover {
    background-color: #f0f0f0;
  }
  
  details[open] summary {
    margin-bottom: 10px;
    border-bottom: 1px solid #ddd;
  }
  
  /* Custom marker */
  summary::marker {
    content: '▶ ';
  }
  
  details[open] summary::marker {
    content: '▼ ';
  }
</style>
Note: The name attribute creates an exclusive accordion where only one details element with the same name can be open at a time (modern browsers only). Use the toggle event to detect state changes.

3. Dialog and Modal Elements

Attribute/Method Purpose Type Modal Behavior
open (attr) Show dialog (non-modal) Attribute No backdrop, no focus trap
show() Show dialog (non-modal) Method No backdrop, no focus trap
showModal() Show dialog (modal) Method Backdrop, focus trap, Esc closes
close() Close dialog Method Returns returnValue

Dialog Properties

Property Description
open Boolean (read-only)
returnValue Value from close()

Dialog Events

Event When Fired
close When dialog closes
cancel When Esc pressed (modal only)

Example: Dialog and modal implementations

<!-- Basic modal dialog -->
<dialog id="myDialog">
  <h2>Dialog Title</h2>
  <p>This is a modal dialog.</p>
  <button onclick="document.getElementById('myDialog').close()">Close</button>
</dialog>

<button onclick="document.getElementById('myDialog').showModal()">Open Modal</button>

<!-- Dialog with form -->
<dialog id="formDialog">
  <form method="dialog">
    <h2>Enter Your Name</h2>
    <input type="text" name="username" required>
    <div>
      <button type="submit" value="cancel">Cancel</button>
      <button type="submit" value="confirm">Confirm</button>
    </div>
  </form>
</dialog>

<button onclick="openFormDialog()">Open Form Dialog</button>

<script>
  const formDialog = document.getElementById('formDialog');
  
  function openFormDialog() {
    formDialog.showModal();
  }
  
  formDialog.addEventListener('close', () => {
    console.log('Dialog closed with:', formDialog.returnValue);
  });
</script>

<!-- Non-modal dialog (show vs showModal) -->
<dialog id="nonModal">
  <p>This is a non-modal dialog. You can still interact with the page.</p>
  <button onclick="document.getElementById('nonModal').close()">Close</button>
</dialog>

<button onclick="document.getElementById('nonModal').show()">Open Non-Modal</button>

<!-- Confirmation dialog -->
<dialog id="confirmDialog">
  <h2>Confirm Action</h2>
  <p>Are you sure you want to delete this item?</p>
  <form method="dialog">
    <button value="no">Cancel</button>
    <button value="yes">Delete</button>
  </form>
</dialog>

<button onclick="showConfirmDialog()">Delete Item</button>

<script>
  const confirmDialog = document.getElementById('confirmDialog');
  
  function showConfirmDialog() {
    confirmDialog.showModal();
  }
  
  confirmDialog.addEventListener('close', () => {
    if (confirmDialog.returnValue === 'yes') {
      console.log('Item deleted');
    } else {
      console.log('Action cancelled');
    }
  });
</script>

<!-- Dialog with backdrop click to close -->
<dialog id="backdropDialog">
  <h2>Click backdrop to close</h2>
  <p>Content here.</p>
  <button onclick="document.getElementById('backdropDialog').close()">Close</button>
</dialog>

<script>
  const backdropDialog = document.getElementById('backdropDialog');
  
  backdropDialog.addEventListener('click', (e) => {
    const rect = backdropDialog.getBoundingClientRect();
    if (
      e.clientX < rect.left ||
      e.clientX > rect.right ||
      e.clientY < rect.top ||
      e.clientY > rect.bottom
    ) {
      backdropDialog.close();
    }
  });
</script>

<!-- Prevent Esc key closing (for critical dialogs) -->
<dialog id="criticalDialog">
  <h2>Critical Action Required</h2>
  <p>You must make a choice.</p>
  <button onclick="document.getElementById('criticalDialog').close('done')">I Understand</button>
</dialog>

<script>
  const criticalDialog = document.getElementById('criticalDialog');
  
  criticalDialog.addEventListener('cancel', (e) => {
    e.preventDefault(); // Prevent Esc from closing
  });
</script>

<!-- Styled dialog -->
<style>
  dialog {
    border: none;
    border-radius: 8px;
    padding: 20px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    max-width: 500px;
  }
  
  dialog::backdrop {
    background-color: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(3px);
  }
  
  dialog h2 {
    margin-top: 0;
  }
  
  dialog button {
    margin: 5px;
    padding: 10px 20px;
  }
</style>
Warning: Use showModal() for true modal behavior (backdrop + focus trap + Esc). show() opens dialog without modal behavior. Forms with method="dialog" automatically close the dialog on submit and set returnValue to the clicked button's value.

4. Progress and Meter Indicators

Element Purpose Use Case Value Type
<progress> Show task progress File upload, loading, completion % Dynamic (0 to max)
<meter> Show measurement in range Disk usage, ratings, gauge Static measurement
Progress Attributes:
Attribute Purpose
value Current progress (0 to max)
max Maximum value (default: 1)

Note: Without value, shows indeterminate state.

Meter Attributes

Attribute Purpose
value Current value
min Minimum value (default: 0)
max Maximum value (default: 1)
low Low threshold
high High threshold
optimum Optimal value

Example: Progress and meter elements

<!-- Progress bar (determinate) -->
<label for="fileProgress">File Upload:</label>
<progress id="fileProgress" value="70" max="100">70%</progress>
<span>70%</span>

<!-- Progress bar (indeterminate) -->
<label>Loading...</label>
<progress></progress>

<!-- Updating progress with JavaScript -->
<progress id="dynamicProgress" value="0" max="100"></progress>
<button onclick="updateProgress()">Start Progress</button>

<script>
  function updateProgress() {
    const progress = document.getElementById('dynamicProgress');
    let value = 0;
    
    const interval = setInterval(() => {
      value += 10;
      progress.value = value;
      
      if (value >= 100) {
        clearInterval(interval);
        alert('Complete!');
      }
    }, 500);
  }
</script>

<!-- Meter: Disk usage (low is bad) -->
<label>Disk Usage:</label>
<meter value="70" min="0" max="100" 
       low="30" high="80" optimum="20">
  70%
</meter>
<span>70 GB of 100 GB used</span>

<!-- Meter: Battery level (high is good) -->
<label>Battery:</label>
<meter value="85" min="0" max="100"
       low="20" high="80" optimum="100">
  85%
</meter>

<!-- Meter: Temperature (middle is optimal) -->
<label>Temperature:</label>
<meter value="72" min="32" max="212"
       low="60" high="80" optimum="70">
  72°F
</meter>

<!-- Rating meter -->
<label>Product Rating:</label>
<meter value="4.5" min="0" max="5"
       low="2" high="4" optimum="5">
  4.5 out of 5
</meter>

<!-- Multiple progress bars -->
<div>
  <label>HTML Skills:</label>
  <progress value="90" max="100"></progress> 90%
</div>
<div>
  <label>CSS Skills:</label>
  <progress value="85" max="100"></progress> 85%
</div>
<div>
  <label>JavaScript Skills:</label>
  <progress value="75" max="100"></progress> 75%
</div>

<!-- Styled progress and meter -->
<style>
  progress, meter {
    width: 200px;
    height: 20px;
    border-radius: 10px;
  }
  
  /* Progress bar styling */
  progress {
    appearance: none;
  }
  
  progress::-webkit-progress-bar {
    background-color: #f0f0f0;
    border-radius: 10px;
  }
  
  progress::-webkit-progress-value {
    background-color: #4caf50;
    border-radius: 10px;
    transition: width 0.3s;
  }
  
  progress::-moz-progress-bar {
    background-color: #4caf50;
    border-radius: 10px;
  }
  
  /* Meter styling */
  meter {
    appearance: none;
  }
  
  meter::-webkit-meter-bar {
    background-color: #f0f0f0;
    border-radius: 10px;
  }
  
  /* Green when optimal */
  meter::-webkit-meter-optimum-value {
    background-color: #4caf50;
  }
  
  /* Yellow when suboptimal */
  meter::-webkit-meter-suboptimum-value {
    background-color: #ffc107;
  }
  
  /* Red when sub-suboptimal */
  meter::-webkit-meter-even-less-good-value {
    background-color: #f44336;
  }
</style>
Note: Use <progress> for tasks that change over time (uploads, downloads). Use <meter> for static measurements (disk usage, ratings). Meter automatically colors based on low, high, and optimum values.

5. Content Editable and Rich Text Editing

Attribute Values Behavior Use Case
contenteditable true, false, plaintext-only Makes element editable Inline editing, WYSIWYG editors
spellcheck true, false Enable/disable spell check Control spell checking

Document.execCommand (deprecated)

Command Action
bold Toggle bold
italic Toggle italic
underline Toggle underline
formatBlock Change block type
insertHTML Insert HTML
createLink Create hyperlink

Selection API (modern)

Method/Property Purpose
getSelection() Get current selection
getRangeAt() Get range object
insertNode() Insert node at selection
deleteContents() Delete selected content

Example: Content editable implementations

<!-- Basic contenteditable -->
<div contenteditable="true" style="border: 1px solid #ccc; padding: 10px;">
  Click here to edit this text.
</div>

<!-- Plaintext only (no formatting) -->
<div contenteditable="plaintext-only" style="border: 1px solid #ccc; padding: 10px;">
  This can only be edited as plain text.
</div>

<!-- Editable with spell check disabled -->
<div contenteditable="true" spellcheck="false">
  Code example without spell checking.
</div>

<!-- Simple WYSIWYG editor -->
<div id="toolbar">
  <button onclick="document.execCommand('bold')"><b>B</b></button>
  <button onclick="document.execCommand('italic')"><i>I</i></button>
  <button onclick="document.execCommand('underline')"><u>U</u></button>
  <button onclick="document.execCommand('insertOrderedList')">OL</button>
  <button onclick="document.execCommand('insertUnorderedList')">UL</button>
  <button onclick="insertLink()">Link</button>
</div>

<div id="editor" contenteditable="true" 
     style="border: 1px solid #ccc; min-height: 200px; padding: 10px;">
  Start typing here...
</div>

<script>
  function insertLink() {
    const url = prompt('Enter URL:');
    if (url) {
      document.execCommand('createLink', false, url);
    }
  }
</script>

<!-- Save and restore content -->
<div id="editableDiv" contenteditable="true" 
     style="border: 1px solid #ccc; padding: 10px;">
  Edit this content and save it.
</div>

<button onclick="saveContent()">Save</button>
<button onclick="loadContent()">Load</button>
<button onclick="clearContent()">Clear</button>

<script>
  function saveContent() {
    const content = document.getElementById('editableDiv').innerHTML;
    localStorage.setItem('savedContent', content);
    alert('Content saved!');
  }
  
  function loadContent() {
    const content = localStorage.getItem('savedContent');
    if (content) {
      document.getElementById('editableDiv').innerHTML = content;
    } else {
      alert('No saved content found.');
    }
  }
  
  function clearContent() {
    document.getElementById('editableDiv').innerHTML = '';
  }
</script>

<!-- Track changes -->
<div id="trackedEditor" contenteditable="true"
     style="border: 1px solid #ccc; padding: 10px; min-height: 100px;">
  Edit me...
</div>
<p>Character count: <span id="charCount">0</span></p>

<script>
  const trackedEditor = document.getElementById('trackedEditor');
  const charCount = document.getElementById('charCount');
  
  trackedEditor.addEventListener('input', () => {
    const text = trackedEditor.textContent;
    charCount.textContent = text.length;
  });
</script>

<!-- Modern Selection API usage -->
<div id="modernEditor" contenteditable="true"
     style="border: 1px solid #ccc; padding: 10px; min-height: 100px;">
  Select text and use buttons below.
</div>

<button onclick="makeSelectionBold()">Make Bold</button>
<button onclick="wrapInSpan()">Wrap in Span</button>

<script>
  function makeSelectionBold() {
    const selection = window.getSelection();
    if (selection.rangeCount > 0) {
      const range = selection.getRangeAt(0);
      const bold = document.createElement('strong');
      range.surroundContents(bold);
    }
  }
  
  function wrapInSpan() {
    const selection = window.getSelection();
    if (selection.rangeCount > 0) {
      const range = selection.getRangeAt(0);
      const span = document.createElement('span');
      span.style.backgroundColor = 'yellow';
      range.surroundContents(span);
    }
  }
</script>

<!-- Prevent certain keys -->
<div contenteditable="true"
     onkeydown="return event.key !== 'Enter'"
     style="border: 1px solid #ccc; padding: 10px;">
  Single line only (Enter disabled)
</div>
Warning: document.execCommand() is deprecated but still widely used. For new projects, use the Selection API and DOM manipulation. Always sanitize user input from contenteditable elements to prevent XSS attacks.

6. Drag and Drop API Integration

Attribute/Event Purpose Applied To Required
draggable Make element draggable Any element Yes (for non-default draggable)
dragstart Drag begins Draggable element Set data
drag While dragging Draggable element Optional
dragend Drag finished Draggable element Cleanup
dragenter Drag enters drop zone Drop target Visual feedback
dragover Drag over drop zone Drop target preventDefault() required
dragleave Drag leaves drop zone Drop target Remove feedback
drop Item dropped Drop target Handle drop

DataTransfer Methods

Method Purpose
setData(type, data) Set drag data
getData(type) Get drag data
clearData() Clear drag data
setDragImage() Custom drag preview

EffectAllowed Values

Value Meaning
copy Copy operation
move Move operation
link Link operation
copyMove Copy or move
all Any operation

Example: Drag and drop implementations

<!-- Basic drag and drop -->
<div id="draggable" draggable="true"
     style="width: 100px; height: 100px; background: #4caf50; color: white;
            display: flex; align-items: center; justify-content: center; cursor: move;">
  Drag Me
</div>

<div id="dropzone"
     style="width: 300px; height: 200px; border: 2px dashed #ccc;
            margin-top: 20px; padding: 20px;">
  Drop Here
</div>

<script>
  const draggable = document.getElementById('draggable');
  const dropzone = document.getElementById('dropzone');
  
  // Drag events
  draggable.addEventListener('dragstart', (e) => {
    e.dataTransfer.setData('text/plain', 'Dragged element');
    e.dataTransfer.effectAllowed = 'move';
    draggable.style.opacity = '0.5';
  });
  
  draggable.addEventListener('dragend', (e) => {
    draggable.style.opacity = '1';
  });
  
  // Drop zone events
  dropzone.addEventListener('dragover', (e) => {
    e.preventDefault(); // Required to allow drop
    e.dataTransfer.dropEffect = 'move';
    dropzone.style.backgroundColor = '#f0f0f0';
  });
  
  dropzone.addEventListener('dragleave', (e) => {
    dropzone.style.backgroundColor = '';
  });
  
  dropzone.addEventListener('drop', (e) => {
    e.preventDefault();
    const data = e.dataTransfer.getData('text/plain');
    dropzone.appendChild(draggable);
    dropzone.style.backgroundColor = '';
  });
</script>

<!-- Sortable list -->
<ul id="sortableList" style="list-style: none; padding: 0;">
  <li draggable="true" style="padding: 10px; margin: 5px; background: #f0f0f0; cursor: move;">Item 1</li>
  <li draggable="true" style="padding: 10px; margin: 5px; background: #f0f0f0; cursor: move;">Item 2</li>
  <li draggable="true" style="padding: 10px; margin: 5px; background: #f0f0f0; cursor: move;">Item 3</li>
  <li draggable="true" style="padding: 10px; margin: 5px; background: #f0f0f0; cursor: move;">Item 4</li>
</ul>

<script>
  const list = document.getElementById('sortableList');
  let draggedItem = null;
  
  list.addEventListener('dragstart', (e) => {
    draggedItem = e.target;
    e.dataTransfer.effectAllowed = 'move';
    e.target.style.opacity = '0.5';
  });
  
  list.addEventListener('dragend', (e) => {
    e.target.style.opacity = '1';
  });
  
  list.addEventListener('dragover', (e) => {
    e.preventDefault();
    const afterElement = getDragAfterElement(list, e.clientY);
    if (afterElement == null) {
      list.appendChild(draggedItem);
    } else {
      list.insertBefore(draggedItem, afterElement);
    }
  });
  
  function getDragAfterElement(container, y) {
    const draggableElements = [...container.querySelectorAll('li:not(.dragging)')];
    
    return draggableElements.reduce((closest, child) => {
      const box = child.getBoundingClientRect();
      const offset = y - box.top - box.height / 2;
      
      if (offset < 0 && offset > closest.offset) {
        return { offset: offset, element: child };
      } else {
        return closest;
      }
    }, { offset: Number.NEGATIVE_INFINITY }).element;
  }
</script>

<!-- File drop zone -->
<div id="fileDropzone"
     style="width: 400px; height: 200px; border: 2px dashed #ccc;
            display: flex; align-items: center; justify-content: center;
            font-size: 18px; color: #999;">
  Drop files here
</div>
<div id="fileList"></div>

<script>
  const fileDropzone = document.getElementById('fileDropzone');
  const fileList = document.getElementById('fileList');
  
  fileDropzone.addEventListener('dragover', (e) => {
    e.preventDefault();
    fileDropzone.style.borderColor = '#4caf50';
    fileDropzone.style.backgroundColor = '#f0f9f0';
  });
  
  fileDropzone.addEventListener('dragleave', (e) => {
    fileDropzone.style.borderColor = '#ccc';
    fileDropzone.style.backgroundColor = '';
  });
  
  fileDropzone.addEventListener('drop', (e) => {
    e.preventDefault();
    fileDropzone.style.borderColor = '#ccc';
    fileDropzone.style.backgroundColor = '';
    
    const files = e.dataTransfer.files;
    fileList.innerHTML = '<h3>Dropped Files:</h3>';
    
    for (let i = 0; i < files.length; i++) {
      const file = files[i];
      fileList.innerHTML += `<p>${file.name} (${file.size} bytes)</p>`;
    }
  });
</script>

<!-- Custom drag image -->
<div id="customDrag" draggable="true"
     style="width: 100px; height: 100px; background: #2196f3; color: white;
            display: flex; align-items: center; justify-content: center; cursor: move;">
  Custom Preview
</div>

<script>
  const customDrag = document.getElementById('customDrag');
  
  customDrag.addEventListener('dragstart', (e) => {
    const dragImage = document.createElement('div');
    dragImage.style.width = '150px';
    dragImage.style.height = '50px';
    dragImage.style.backgroundColor = '#ff5722';
    dragImage.style.color = 'white';
    dragImage.style.display = 'flex';
    dragImage.style.alignItems = 'center';
    dragImage.style.justifyContent = 'center';
    dragImage.textContent = 'Custom Drag Image';
    document.body.appendChild(dragImage);
    
    e.dataTransfer.setDragImage(dragImage, 75, 25);
    
    setTimeout(() => {
      document.body.removeChild(dragImage);
    }, 0);
  });
</script>
Note: Must call preventDefault() in dragover event to allow drop. Use dataTransfer.files to access dropped files. Images, links, and text are draggable by default; other elements need draggable="true".

Section 10 Key Takeaways

  • Always specify type attribute on buttons (submit, button, reset) to avoid unexpected behavior
  • Use <button> over <input type="button"> for more flexible content (HTML, icons)
  • Details/summary provides native accordion without JavaScript; use name attribute for exclusive groups
  • Use showModal() for true modal dialogs (backdrop + focus trap); show() for non-modal
  • Forms with method="dialog" automatically close dialog and set returnValue
  • Use <progress> for dynamic tasks, <meter> for static measurements
  • document.execCommand() is deprecated; use Selection API for modern rich text editing
  • Always sanitize contenteditable content to prevent XSS attacks
  • Must call preventDefault() in dragover event to enable dropping