Updated SKILL.md
This commit is contained in:
@@ -348,6 +348,124 @@ def mk_content(self):
|
||||
|
||||
---
|
||||
|
||||
### DEV-CONTROL-20: HTMX Ajax Requests
|
||||
|
||||
**Always specify a `target` in HTMX ajax requests.**
|
||||
|
||||
```javascript
|
||||
// ❌ INCORRECT: Without target, HTMX doesn't know where to swap the response
|
||||
htmx.ajax('POST', '/url', {
|
||||
values: {param: value}
|
||||
});
|
||||
|
||||
// ✅ CORRECT: Explicitly specify the target
|
||||
htmx.ajax('POST', '/url', {
|
||||
target: '#element-id',
|
||||
values: {param: value}
|
||||
});
|
||||
```
|
||||
|
||||
**Exception:** Response contains elements with `hx-swap-oob="true"`.
|
||||
|
||||
---
|
||||
|
||||
### DEV-CONTROL-21: HTMX Swap Modes and Event Listeners
|
||||
|
||||
**`hx-on::after-settle` only works when the swapped element replaces the target (`outerHTML`).**
|
||||
|
||||
```javascript
|
||||
// ❌ INCORRECT: innerHTML (default) nests the returned element
|
||||
// The hx-on::after-settle attribute on the returned element is never processed
|
||||
htmx.ajax('POST', '/url', {
|
||||
target: '#my-element'
|
||||
// swap: 'innerHTML' is the default
|
||||
});
|
||||
|
||||
// ✅ CORRECT: outerHTML replaces the entire element
|
||||
// The hx-on::after-settle attribute on the returned element works
|
||||
htmx.ajax('POST', '/url', {
|
||||
target: '#my-element',
|
||||
swap: 'outerHTML'
|
||||
});
|
||||
```
|
||||
|
||||
**Why:**
|
||||
- `innerHTML`: replaces **content** → `<div id="X"><div id="X" hx-on::...>new</div></div>` (duplicate ID)
|
||||
- `outerHTML`: replaces **element** → `<div id="X" hx-on::...>new</div>` (correct)
|
||||
|
||||
---
|
||||
|
||||
### DEV-CONTROL-22: Reinitializing Event Listeners
|
||||
|
||||
**After an HTMX swap, event listeners attached via JavaScript are lost and must be reinitialized.**
|
||||
|
||||
**Recommended pattern:**
|
||||
|
||||
1. Create a reusable initialization function:
|
||||
```javascript
|
||||
function initMyControl(controlId) {
|
||||
const element = document.getElementById(controlId);
|
||||
// Attach event listeners
|
||||
element.addEventListener('click', handleClick);
|
||||
}
|
||||
```
|
||||
|
||||
2. Call this function after swap via `hx-on::after-settle`:
|
||||
```python
|
||||
extra_attr = {
|
||||
"hx-on::after-settle": f"initMyControl('{self._id}');"
|
||||
}
|
||||
element.attrs.update(extra_attr)
|
||||
```
|
||||
|
||||
**Alternative:** Use event delegation on a stable parent element.
|
||||
|
||||
---
|
||||
|
||||
### DEV-CONTROL-23: Avoiding Duplicate IDs with HTMX
|
||||
|
||||
**If the element returned by the server has the same ID as the HTMX target, use `swap: 'outerHTML'`.**
|
||||
|
||||
```python
|
||||
# Server returns an element with id="my-element"
|
||||
def render_partial(self):
|
||||
return Div(id="my-element", ...) # Same ID as target
|
||||
|
||||
# JavaScript must use outerHTML
|
||||
htmx.ajax('POST', '/url', {
|
||||
target: '#my-element',
|
||||
swap: 'outerHTML' # ✅ Replaces the entire element
|
||||
});
|
||||
```
|
||||
|
||||
**Why:** `innerHTML` would create `<div id="X"><div id="X">...</div></div>` (invalid duplicate ID).
|
||||
|
||||
---
|
||||
|
||||
### DEV-CONTROL-24: Pattern extra_attr for HTMX
|
||||
|
||||
**Use the `extra_attr` pattern to add post-swap behaviors.**
|
||||
|
||||
```python
|
||||
def render_partial(self, fragment="default"):
|
||||
extra_attr = {
|
||||
"hx-on::after-settle": f"initControl('{self._id}');",
|
||||
# Other HTMX attributes if needed
|
||||
}
|
||||
|
||||
element = self.mk_element()
|
||||
element.attrs.update(extra_attr)
|
||||
return element
|
||||
```
|
||||
|
||||
**Use cases:**
|
||||
- Reinitialize event listeners
|
||||
- Execute animations
|
||||
- Update other DOM elements
|
||||
- Logging or tracking events
|
||||
|
||||
---
|
||||
|
||||
## Complete Control Template
|
||||
|
||||
```python
|
||||
|
||||
Reference in New Issue
Block a user