11 KiB
Mouse Support - Documentation
Overview
The mouse support library provides keyboard-like binding capabilities for mouse actions. It supports simple clicks, modified clicks (with Ctrl/Shift/Alt), and sequences of clicks with smart timeout logic.
Features
Supported Mouse Actions
Basic Actions:
click- Left clickright_click(orrclick) - Right click (contextmenu)
Modified Actions:
ctrl+click(orctrl+rclick) - Ctrl+Click (or Cmd+Click on Mac)shift+click(orshift+rclick) - Shift+Clickalt+click(oralt+rclick) - Alt+Clickctrl+shift+click- Multiple modifiers- Any combination of modifiers
Sequences:
click right_click(orclick rclick) - Click then right-click within 500msclick click- Double click sequencectrl+click click- Ctrl+click then normal click- Any sequence of actions
Note: rclick is an alias for right_click and can be used interchangeably.
Smart Timeout Logic
Same as keyboard support:
- If any element has a longer sequence possible, all matching elements wait
- Timeout is 500ms between actions
- Immediate trigger if no longer sequences exist
Multiple Element Support
Multiple elements can listen to the same mouse action and all will trigger simultaneously.
Configuration Format
Uses HTMX configuration objects (same as keyboard support):
const combinations = {
"click": {
"hx-post": "/handle-click",
"hx-target": "#result"
},
"ctrl+click": {
"hx-post": "/handle-ctrl-click",
"hx-swap": "innerHTML"
},
"rclick": { // Alias for right_click
"hx-post": "/context-menu"
},
"click rclick": { // Can use rclick in sequences too
"hx-post": "/sequence-action",
"hx-vals": {"type": "sequence"}
}
};
add_mouse_support('my-element', JSON.stringify(combinations));
API Reference
add_mouse_support(elementId, combinationsJson)
Adds mouse support to an element.
Parameters:
elementId(string): ID of the HTML elementcombinationsJson(string): JSON string of combinations with HTMX configs
Returns: void
Example:
add_mouse_support('button1', JSON.stringify({
"click": {"hx-post": "/click"},
"ctrl+click": {"hx-post": "/ctrl-click"}
}));
remove_mouse_support(elementId)
Removes mouse support from an element.
Parameters:
elementId(string): ID of the HTML element
Returns: void
Side effects:
- If last element: detaches global event listeners and cleans up all state
Example:
remove_mouse_support('button1');
Automatic Parameters
The library automatically adds these parameters to every HTMX request:
combination- The mouse combination that triggered the action (e.g., "ctrl+click")has_focus- Boolean indicating if the element had focus when clickedis_inside- Boolean indicating if the click was inside the element- For
click:trueif clicked inside element,falseif clicked outside - For
right_click: alwaystrue(only triggers when clicking on element)
- For
has_focus- Boolean indicating if the element had focus when the action triggeredclicked_inside- Boolean indicating if the click was inside the element or outside
Parameter Details
has_focus:
trueif the registered element currently has focusfalseotherwise- Useful for knowing if the element was the active element
clicked_inside:
- For
clickactions:trueif clicked on/inside the element,falseif clicked outside - For
right_clickactions: alwaystrue(since right-click only triggers on the element) - Useful for "click outside to close" logic
Example values sent:
// User clicks inside a modal
{
combination: "click",
has_focus: true,
clicked_inside: true
}
// User clicks outside the modal (modal still gets triggered because click is global)
{
combination: "click",
has_focus: false,
clicked_inside: false // Perfect for closing the modal!
}
// User right-clicks on an item
{
combination: "right_click",
has_focus: false,
clicked_inside: true // Always true for right_click
}
Python Integration
Basic Usage
combinations = {
"click": {
"hx-post": "/item/select"
},
"ctrl+click": {
"hx-post": "/item/select-multiple",
"hx-vals": json.dumps({"mode": "multi"})
},
"right_click": {
"hx-post": "/item/context-menu",
"hx-target": "#context-menu",
"hx-swap": "innerHTML"
}
}
f"add_mouse_support('{element_id}', '{json.dumps(combinations)}')"
Sequences
combinations = {
"click": {
"hx-post": "/single-click"
},
"click click": {
"hx-post": "/double-click-sequence"
},
"click right_click": {
"hx-post": "/click-then-right-click"
}
}
Multiple Elements
# Item 1
item1_combinations = {
"click": {"hx-post": f"/item/1/select"},
"ctrl+click": {"hx-post": f"/item/1/toggle"}
}
f"add_mouse_support('item-1', '{json.dumps(item1_combinations)}')"
# Item 2
item2_combinations = {
"click": {"hx-post": f"/item/2/select"},
"ctrl+click": {"hx-post": f"/item/2/toggle"}
}
f"add_mouse_support('item-2', '{json.dumps(item2_combinations)}')"
Behavior Details
Click vs Right-Click Behavior
IMPORTANT: The library handles click and right_click differently:
click (global detection):
- Triggers for ALL registered elements, regardless of where you click
- Useful for "click outside to close" functionality (modals, dropdowns, popups)
- Example: Modal registered with
click→ clicking anywhere on the page triggers the modal's click action
right_click (element-specific detection):
- Triggers ONLY when you right-click on (or inside) the registered element
- Right-clicking outside the element does nothing and shows browser's context menu
- This preserves normal browser behavior while adding custom actions on your elements
Example use case:
// Modal that closes when clicking anywhere
add_mouse_support('modal', JSON.stringify({
"click": {"hx-post": "/close-modal"} // Triggers even if you click outside modal
}));
// Context menu that only appears on element
add_mouse_support('item', JSON.stringify({
"right_click": {"hx-post": "/item-menu"} // Only triggers when right-clicking the item
}));
Modifier Keys (Cross-Platform)
- Windows/Linux:
ctrl+clickuses Ctrl key - Mac:
ctrl+clickuses Cmd (⌘) key OR Ctrl key - This follows standard web conventions for cross-platform compatibility
Input Context Protection
Mouse actions are disabled when clicking in input fields:
<input>elements<textarea>elements- Any
contenteditableelement
This ensures normal text selection and interaction works in forms.
Right-Click Menu
The contextmenu (right-click menu) is prevented with preventDefault() when:
- A
right_clickaction is configured for the element - The element is NOT an input/textarea/contenteditable
Event Bubbling
The library checks if the click target OR any parent element is registered:
<div id="container">
<button>Click me</button>
</div>
If container is registered, clicking the button will trigger the container's actions.
Examples
Using Aliases
You can use rclick instead of right_click anywhere:
// These are equivalent
const config1 = {
"right_click": {"hx-post": "/menu"}
};
const config2 = {
"rclick": {"hx-post": "/menu"} // Shorter alias
};
// Works in sequences too
const config3 = {
"click rclick": {"hx-post": "/sequence"}
};
// Works with modifiers
const config4 = {
"ctrl+rclick": {"hx-post": "/ctrl-right-click"}
};
Context Menu
const combinations = {
"right_click": {
"hx-post": "/show-context-menu",
"hx-target": "#menu",
"hx-swap": "innerHTML"
}
};
Close Modal/Popup on Click Outside
Since click is detected globally (anywhere on the page), it's perfect for "click outside to close":
// Modal element
const modalCombinations = {
"click": {
"hx-post": "/close-modal",
"hx-target": "#modal",
"hx-swap": "outerHTML"
}
};
add_mouse_support('modal', JSON.stringify(modalCombinations));
// Now clicking ANYWHERE on the page will trigger the handler
// Your backend receives:
// - clicked_inside: true (if clicked on modal) → maybe keep it open
// - clicked_inside: false (if clicked outside) → close it!
Backend example (Python/Flask):
@app.route('/close-modal', methods=['POST'])
def close_modal():
clicked_inside = request.form.get('clicked_inside') == 'true'
if clicked_inside:
# User clicked inside the modal - keep it open
return render_template('modal_content.html')
else:
# User clicked outside - close the modal
return '' # Empty response removes the modal
Note: The click handler on the modal element will trigger for all clicks on the page, not just clicks on the modal itself. Use the clicked_inside parameter to determine the appropriate action.
Multi-Select List
const combinations = {
"click": {
"hx-post": "/select-item",
"hx-vals": {"mode": "single"}
},
"ctrl+click": {
"hx-post": "/select-item",
"hx-vals": {"mode": "toggle"}
},
"shift+click": {
"hx-post": "/select-range",
"hx-vals": {"mode": "range"}
}
};
Interactive Canvas/Drawing
const combinations = {
"click": {
"hx-post": "/draw-point"
},
"ctrl+click": {
"hx-post": "/draw-special"
},
"click click": {
"hx-post": "/confirm-action"
}
};
Drag-and-Drop Alternative
const combinations = {
"click": {
"hx-post": "/select-source"
},
"click click": {
"hx-post": "/set-destination"
}
};
Troubleshooting
Clicks not detected
- Verify the element exists and has the correct ID
- Check browser console for errors
- Ensure HTMX is loaded before mouse_support.js
Right-click menu still appears
- Check if element is in input context (input/textarea)
- Verify the combination is configured correctly
- Check browser console for configuration errors
Sequences not working
- Ensure clicks happen within 500ms timeout
- Check if longer sequences exist (causes waiting)
- Verify the combination string format (space-separated)
Technical Details
Architecture
- Global listeners on
documentforclickandcontextmenuevents - Tree-based matching using prefix trees (same as keyboard support)
- Single timeout for all elements (sequence-based, not element-based)
- Independent from keyboard support (separate registry and timeouts)
Performance
- Single event listener regardless of number of elements
- O(n) matching where n is sequence length
- Efficient memory usage with automatic cleanup
Browser Compatibility
- Modern browsers (ES6+ required)
- Chrome, Firefox, Safari, Edge
- Requires HTMX library
Notes
- Timeout value is the same as keyboard support (500ms) but in separate variable
- Can be used independently or alongside keyboard support
- Does not interfere with normal mouse behavior in inputs
- Element must exist in DOM when
add_mouse_support()is called - Alias:
rclickcan be used interchangeably withright_clickfor shorter syntax