Popovers and action menus
Build a reusable task action panel
Reuse one popover across task cards and update its target context before the panel opens.
Just like one dialog can edit many tasks, one popover can serve the More actions button on every card. The important part is connecting the shared panel to the task whose button opened it.
A delegated click handler records the task ID before the browser toggles the popover. The action buttons then operate on that ID. The popover’s toggle event clears stale context when it closes, so a later keyboard or scripted action does not touch the previous task.
const board = document.querySelector('#board')
const taskActions = document.querySelector('#task-actions')
let actionTaskId = null
board.addEventListener('click', event => {
const opener = event.target.closest('button[popovertarget=task-actions]')
if (opener) actionTaskId = opener.closest('[data-task-id]').dataset.taskId
})
taskActions.addEventListener('click', event => {
const action = event.target.closest('button[data-action]')?.dataset.action
if (action && actionTaskId) runTaskAction(actionTaskId, action)
taskActions.hidePopover()
})
taskActions.addEventListener('toggle', event => {
if (event.newState === 'closed') actionTaskId = null
})
Open More actions on task-17, pick Duplicate, and runTaskAction should receive task-17, not whatever card you used last time.
Give every More actions button a name that includes context when repeated controls would otherwise sound the same. aria-label="More actions for Write homepage" is direct. The visible task heading helps too, but explicit names improve navigation through controls.
Keep focus predictable. Opening a simple popover does not automatically mean focus must jump into it. For a panel with several actions, moving focus to its first button may help. After an action or dismissal, return to the invoker if it still exists.
Do not close the panel before an action has captured the task ID. hidePopover() can fire state changes fast enough that a cleanup handler clears shared context first. Pass the ID into the action function first, or copy it to a local constant.
Try this on three task cards with the same popovertarget: run Duplicate or Archive from the shared panel, open from one card, dismiss, then open from another. If the first task changes, inspect the order in which context is assigned, used, and cleared.
Lesson completed