Worked Example: An Interactive List

This section documents jQuery 3.x (the current major line — no specific patch version is pinned). This content was generated with the assistance of AI and should be verified against the official jQuery API reference before being relied on in production, since API details and deprecations continue to change between releases.

jQuery is legacy-leaning: modern browsers implement native equivalents for almost everything it does (querySelectorAll, classList, fetch, addEventListener, append/prepend/remove, the Web Animations API), and most current stacks — Bootstrap 5 included — have dropped it. Prefer the native APIs for new work; this reference is aimed at reading, maintaining, or incrementally migrating code that already uses jQuery. Every page ends with a Modern equivalent note pointing at the native replacement.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page ties the section together: one small feature — a dynamic shopping list with add, remove, and reorder — that uses selection, DOM manipulation, events, and effects at once. The whole thing is one HTML file.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Interactive list</title>
  <style>
    body { font: 16px/1.4 system-ui, sans-serif; max-width: 32rem; margin: 2rem auto; }
    li { display: flex; gap: .5rem; align-items: center; padding: .25rem 0; }
    li.done span { text-decoration: line-through; opacity: .6; }
    li span { flex: 1; }
    button { cursor: pointer; }
  </style>
</head>
<body>
  <h1>Shopping list</h1>

  <form id="add-form">
    <input id="add-input" type="text" placeholder="Add an item" autocomplete="off" required>
    <button type="submit">Add</button>
  </form>

  <ul id="list"></ul>

  <script
    src="https://code.jquery.com/jquery-3.7.1.min.js"
    integrity="sha256-..."
    crossorigin="anonymous"></script>
  <script>
  $(function () {
    const $list = $('#list');
    const $input = $('#add-input');

    // --- ADD: form submit handler (events page) ---
    $('#add-form').on('submit', function (event) {
      event.preventDefault();                 // don't reload the page
      const text = $input.val().trim();
      if (!text) return;

      // build the row with the tag + attributes object (dom-manipulation page)
      const $row = $('<li>', { html:
        '<span></span>' +
        '<button class="up" title="Move up">&uarr;</button>' +
        '<button class="down" title="Move down">&darr;</button>' +
        '<button class="del" title="Delete">&times;</button>'
      });
      $row.find('span').text(text);            // .text() escapes -- safe for user input

      $row.hide().appendTo($list).slideDown(150);   // effects page
      $input.val('').focus();
    });

    // --- DELEGATED handlers: one listener on #list for every current AND future row ---
    // (events page: delegation + traversal page: .closest())

    $list.on('click', '.del', function () {
      $(this).closest('li').slideUp(150, function () { $(this).remove(); });
    });

    $list.on('click', 'span', function () {
      $(this).closest('li').toggleClass('done');   // css-attributes page: toggleClass
    });

    $list.on('click', '.up', function () {
      const $li = $(this).closest('li');
      $li.prev('li').before($li);              // traversal: .prev() + manipulation: .before()
    });

    $list.on('click', '.down', function () {
      const $li = $(this).closest('li');
      $li.next('li').after($li);
    });
  });
  </script>
</body>
</html>

Walkthrough

Add

The submit handler (Events) calls event.preventDefault() so the form does not navigate. A new <li> is built with the $('<tag>', props) form from DOM Manipulation; the user’s text goes in through .text(), which escapes markup, so a pasted <script> is inert. .hide().appendTo(…​).slideDown() (Effects and Animation) inserts it invisibly and animates it open.

Remove / toggle / reorder

All four are delegated: the handler is bound once to #list, with a selector (.del, span, .up, .down) as the second argument. Rows added later are handled with no re-binding — the reason delegation is the default pattern for dynamic lists (Events). Each handler uses .closest('li') (Traversal and Chaining) to get from the clicked button up to its row. Reordering combines a traversal step (.prev('li') / .next('li')) with an insertion step (.before() / .after()) from DOM Manipulation; moving an element that is already in the DOM relocates it rather than copying it.

Chaining

$row.hide().appendTo($list).slideDown(150) and $li.prev('li').before($li) both rely on every method returning a jQuery set (Traversal and Chaining).

Modern equivalent

The same feature in vanilla JS: form.addEventListener('submit', …​), document.createElement('li'), list.addEventListener('click', e ⇒ { const li = e.target.closest('li'); …​ }) for delegation, el.classList.toggle('done'), and refNode.before(li) / after(li) for reordering — plus a CSS transition instead of slideDown/slideUp. See Web programming basics and Events.