**Implementation Plan** **Scope:** Resolve the intermittent unresponsiveness of vote/star buttons on dynamically inserted content (comments, infinite scroll cards) by adopting document-level event delegation in `VoteManager.js`, consistent with existing patterns in `ReactionBar.js` and `BookmarkManager.js`. Additionally, improve UX for the session-expiry cause by adding a `title` attribute to disabled vote buttons. --- ### Changes **1. `devplacepy/static/js/VoteManager.js` – Replace one-shot attachment with delegation** - Locate the `initVoteButtons()` method (currently uses `querySelectorAll` + per-element listeners). - Remove the per-element loop. - Add a constructor that registers one delegated `click` listener on `document`: ```js constructor() { super(); document.addEventListener("click", (event) => { const button = event.target.closest('form[action^="/votes/"] button[type="submit"]'); if (!button) return; const form = button.closest("form"); if (!form) return; event.preventDefault(); event.stopPropagation(); this.cast(form, button); }); } ``` - Remove the `initVoteButtons()` method entirely (or leave a no-op stub with a comment marking it deprecated). - Ensure `window.VoteManager` instantiation (or wherever `initVoteButtons()` is called) does not break; the constructor performs delegation automatically. **2. `devplacepy/static/js/_star_vote.html` and `devplacepy/templating.py` – Add `title` attribute to disabled vote buttons** - In the template helper function `guest_disabled(user)`, include a `title="Log in to vote"` attribute when `disabled` is True. - Alternatively, if the helper returns only the `disabled` attribute, update each template that uses it to add a `title` conditionally. Simpler: add the `title` inside `templating.py`’s `guest_disabled()` output: ```python def guest_disabled(user): if not user: return 'disabled title="Log in to vote"' return '' ``` - Verify that no existing CSS or JS relies on the absence of `title`; this is a purely additive, non-breaking change. **3. No other files require modification** – the CSS stacking context, `CommentManager.js`, and `PollManager.js` are already correct. --- ### Definition of Done - [ ] `VoteManager.js` is updated to use a single document-level delegated `click` listener per the pattern above; the old `initVoteButtons()` is removed. - [ ] On initial page load, vote/star buttons behave identically to before (optimistic update, toggling, etc.). - [ ] Dynamically inserted vote buttons (from AJAX comments, infinite scroll, or any future dynamic content) are now responsive and produce the same optimistic UX. - [ ] All existing vote e2e tests (in `tests/e2e/gist.py` and `tests/e2e/leaderboard.py`) still pass without regression. - [ ] The `guest_disabled()` helper (in `templating.py`) outputs `disabled title="Log in to vote"` for unauthenticated users, providing a tooltip explaining why the button is inactive. - [ ] Unit tests pass: `pip install -e '.[dev]' -q && make test-unit` returns exit code 0. - [ ] Linting passes: `pip install -q ruff && ruff check .` returns exit code 0. - [ ] No new lint warnings or errors are introduced (ruff check clean).