bhylu.com

Advertisements

jQuery Checklist Exercise

Practise event delegation, stable row identity and a derived count before viewing the worked answer.

Your brief

Create two book rows with checkboxes and show how many titles are checked. Derive the count from the current controls instead of retaining a second counter that can drift. Add a button that appends a third row without needing another event-binding pass. Use a scoped delegated change handler and assign each label as text. The task is a local checklist, not a stored reading history or course-completion record.

Run in a separate local browser lab

Save this complete HTML source as index.html in an empty folder. Put the pinned full jQuery 4.0.0 file beside it and preserve the MIT licence notice. The document includes its own CSS and JavaScript. Bhylu’s shared website theme does not load jQuery globally.

Open index.html directly in a normal browser. You may also use the optional loopback fixture server with Node 22 if you want the same HTTP setup as the network lessons.

jQuery examples do not run in Bhylu’s current playground: it blocks external libraries and network access. No Apache, MySQL or website configuration change is needed. Review the full-build setup before using Ajax, effects or plugins.

Worked solution

Show the worked solution
index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>jQuery Build a Local Reading Checklist</title>
    <style>
      :root {
        font:
          18px/1.6 system-ui,
          sans-serif;
        color: #16304e;
        background: #fff;
        color-scheme: light;
      }
      * {
        box-sizing: border-box;
      }
      body {
        margin: 0;
      }
      main {
        max-width: 50rem;
        margin: auto;
        padding: 1.25rem;
      }
      h1 {
        font-size: 1.8rem;
        line-height: 1.2;
      }
      h2 {
        font-size: 1.3rem;
      }
      button,
      input,
      select,
      textarea {
        font: inherit;
        max-width: 100%;
      }
      button {
        margin: 0.2rem;
        padding: 0.4rem 0.7rem;
        background: #034fa0;
        color: white;
        border: 1px solid #034fa0;
        border-radius: 0.3rem;
      }
      button:disabled {
        opacity: 0.6;
      }
      label {
        display: block;
        margin: 0.5rem 0;
      }
      input:not([type='checkbox']),
      select,
      textarea {
        display: block;
        padding: 0.3rem;
      }
      a {
        color: #034fa0;
      }
      output {
        display: block;
      }
      article,
      section,
      fieldset {
        padding: 0.75rem;
        margin-block: 0.75rem;
        border: 1px solid #a9bcd1;
      }
      ul {
        padding-left: 1.5rem;
      }
      :focus-visible {
        outline: 3px solid #0078d4;
        outline-offset: 3px;
      }
      .chosen {
        border: 3px solid #0078d4;
      }
      .highlight {
        font-weight: 700;
      }
      .muted {
        opacity: 0.7;
      }
      pre {
        white-space: pre-wrap;
        overflow-wrap: anywhere;
      }
      :root[data-theme='dark'] {
        color: #d6e7fb;
        background: #061d30;
        color-scheme: dark;
      }
      :root[data-theme='dark'] a {
        color: #8ecbff;
      }
    </style>
  </head>
  <body>
    <main>
      <h1>Neighbourhood book swap</h1>
      <ul id="books">
        <li>
          <label><input type="checkbox" />River Paths</label>
        </li>
        <li>
          <label><input type="checkbox" />Small Gardens</label>
        </li>
      </ul>
      <button id="add">Add Evening Stories</button><output>0 checked</output>
    </main>
    <script src="jquery.min.js"></script>
    <script>
      jQuery(function ($) {
        function report() {
          $('output').text($('#books input:checked').length + ' checked');
        }
        $('#books').on('change.checklist', 'input[type=checkbox]', report);
        $('#add').one('click', function () {
          const $label = $('<label>')
            .append($('<input>', { type: 'checkbox' }))
            .append(document.createTextNode('Evening Stories'));
          $('<li>').append($label).appendTo('#books');
        });
      });
    </script>
  </body>
</html>

Expected result

Existing and newly added rows contribute to the same checked count.

The added row is handled by the existing delegated root. one limits this particular add action to one insertion; it is not a substitute for a general duplicate-record policy. A label wraps its checkbox so clicking the visible title activates the control. The count is read from checked properties through the selector and is not guessed from the number of clicks.

Extend and test

Add a Clear checks button using prop, then call the same report function explicitly. Verify both old and newly inserted rows reset.

Use jQuery’s official API documentation for exact method and return contracts.

Advertisements