4.0 KiB
Shot Delete Event Bubbling Fix
Issue Description
When clicking the "Delete Shot" button in the ShotsDataTable, the shot detail panel was being triggered along with the delete action. This was happening because the delete button click event was bubbling up to the table row click handler, which opens the shot detail panel.
Root Cause
The issue was caused by event bubbling in the table row structure:
- The table row has a
@click="handleRowClick"event handler that opens the shot detail panel - The delete button (inside a dropdown menu) is within the table row
- Even though the dropdown menu items had
e.stopPropagation(), the event was still bubbling up to the row click handler
Solution
1. Enhanced Event Handling in ShotsDataTable
Modified the handleRowClick function in ShotsDataTable.vue to check if the click target is within an interactive element:
const handleRowClick = (shot: Shot, event: MouseEvent) => {
// Check if the click target is within a dropdown menu, button, or other interactive element
const target = event.target as HTMLElement
if (target) {
// Check if the click is on a button, dropdown, or any interactive element
const isInteractiveElement = target.closest('button, [role="menuitem"], [data-radix-collection-item]')
if (isInteractiveElement) {
// Don't emit row-click if clicking on interactive elements
return
}
}
emit('row-click', shot, event)
}
This approach:
- Checks if the click target is within a button, dropdown menu item, or other interactive element
- Uses
closest()to traverse up the DOM tree to find interactive elements - Prevents the row-click event from being emitted when clicking on interactive elements
2. Improved Event Prevention in Columns
Enhanced the event handling in columns.ts for all dropdown menu items:
// Before
onClick: (e: Event) => { e.stopPropagation(); meta.onDelete(shot) }
// After
onClick: (e: Event) => {
e.stopPropagation()
e.preventDefault()
meta.onDelete(shot)
}
Added e.preventDefault() in addition to e.stopPropagation() for more robust event handling.
3. Enhanced Dropdown Trigger
Improved the dropdown trigger button event handling:
h(
DropdownMenuTrigger,
{
asChild: true,
onClick: (e: Event) => {
e.stopPropagation()
e.preventDefault()
}
},
{
default: () =>
h(
Button,
{
variant: 'ghost',
size: 'sm',
class: 'h-8 w-8 p-0',
onClick: (e: Event) => {
e.stopPropagation()
e.preventDefault()
}
},
// ...
),
}
)
Testing
Created test-shot-delete-event-fix.html to verify the fix works correctly:
- Clicking the "Delete Shot" button should NOT trigger the row click event
- Clicking anywhere else on the row SHOULD trigger the row click event
- The event log shows which events are triggered and which are prevented
Files Modified
-
frontend/src/components/shot/ShotsDataTable.vue- Enhanced
handleRowClickfunction to detect interactive elements
- Enhanced
-
frontend/src/components/shot/columns.ts- Added
e.preventDefault()to all dropdown menu item click handlers - Enhanced dropdown trigger event handling
- Added
Benefits
- Prevents unwanted shot detail panel opening when deleting shots
- Maintains proper row click functionality for non-interactive areas
- Provides a robust solution that works with various UI components
- Uses DOM traversal to detect interactive elements, making it future-proof
Prevention
To prevent similar issues in the future:
- Always use both
e.stopPropagation()ande.preventDefault()for interactive elements within clickable containers - Consider using DOM traversal (
closest()) to detect interactive elements in container click handlers - Test interactive elements within clickable containers to ensure proper event handling
- Use specific selectors for interactive elements (buttons, menu items, etc.)