Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion nojs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ Special variables: `$event` (native Event), `$el` (current element).
| `then` | `then="expr"` | Expression to run on success |
| `error-boundary` | `error-boundary="tplId"` | Catch errors in subtree |

**Pagination**: `get-trigger="scroll"` (infinite) or `"button"` (load more), `get-insert="append"`, `get-page="1"` (offset) or `get-cursor` + `get-cursor-field` (cursor), `get-threshold="200"` (scroll px).
**Pagination**: `get-trigger="scroll"` (infinite) or `"button"` (load more), `get-insert="append"`, `get-page="1"` (offset) or `get-cursor` + `get-cursor-field` (cursor), `get-threshold="200"` (scroll px). Observer root is the nearest scrollable ancestor (`overflow-y: auto|scroll`), falling back to the viewport; `get-threshold` is relative to that container. A not-yet-overflowing container triggers loads until content fills it (fill-until-overflow).

URLs support interpolation: `get="/users/{userId}"`. Reactive expressions in URLs automatically re-fetch.

Expand Down
10 changes: 7 additions & 3 deletions nojs/references/directives/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,10 +444,10 @@ Controls when the GET request fires.
| Value | Behavior |
|-------|----------|
| (absent) | Fetches immediately on mount (default `get` behavior) |
| `visible` | Fetches when element enters viewport via IntersectionObserver |
| `visible` | Fetches when element becomes visible in its scroll container via IntersectionObserver |
| `hover` | Fetches on first `mouseenter` event |
| `none` | Suppresses auto-fetch; use `.refresh()` to trigger manually |
| `scroll` | Infinite scroll — fetches next page when sentinel enters viewport. Requires `get-insert` |
| `scroll` | Infinite scroll — fetches next page when sentinel becomes visible in the scroll container. Requires `get-insert` |
| `button` | Renders a "Load More" button. Requires `get-insert` |

```html
Expand Down Expand Up @@ -559,8 +559,12 @@ IntersectionObserver `rootMargin` for `scroll` and `visible` triggers.

Controls how early the trigger fires. Default: `200px` for `scroll`, `0px` for `visible`.

**Observer root:** The IntersectionObservers used by `get-trigger="scroll"` (sentinel observer) and `get-trigger="visible"` -- as well as the fallback observer used when `get-trigger="scroll"` is set without an insert mode -- all resolve the nearest ancestor element with computed `overflow-y: auto` or `overflow-y: scroll` as the observer root. If no scrollable ancestor is found, the document (viewport) is used. Because the root is the scroll container, `get-threshold` is relative to that container's bounds, not the browser viewport.

**Fill-until-overflow:** When `get-trigger="scroll"` targets a container that has not yet overflowed (content is shorter than the container), the sentinel is immediately visible within the scroll container. This causes successive loads until content grows enough to push the sentinel out of view -- correct infinite-scroll semantics. Once the container overflows, loads pause and only resume when actual scrolling brings the sentinel back into view.

```html
<!-- Start loading 500px before element enters viewport -->
<!-- Start loading 500px before element scrolls into view -->
<div get="/api/heavy-data"
get-trigger="visible"
get-threshold="500px"
Expand Down
6 changes: 3 additions & 3 deletions nojs/references/patterns/data-fetching.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ Uses `watch` + `debounce` on a reactive URL to fire requests 300ms after typing
Uses Core's built-in pagination directives (`get-trigger="scroll"`, `get-insert`, `get-page`) instead of manual JavaScript. The framework handles IntersectionObserver, page tracking, and end-of-data detection automatically.

```html
<!-- Infinite scroll — Core handles viewport detection and page incrementing -->
<!-- Infinite scroll — Core handles scroll-container detection and page incrementing -->
<div get="/api/feed?page={page}" as="items"
get-trigger="scroll"
get-insert="append"
Expand Down Expand Up @@ -171,13 +171,13 @@ Uses Core's built-in pagination directives (`get-trigger="scroll"`, `get-insert`
</template>
```

> **How it works:** `get-trigger="scroll"` creates an IntersectionObserver on a sentinel element. When the sentinel enters the viewport (controlled by `get-threshold`), the next page is fetched and appended. Pagination stops automatically when the server returns an empty response.
> **How it works:** `get-trigger="scroll"` creates an IntersectionObserver on a sentinel element. When the sentinel becomes visible in the nearest scrollable ancestor (controlled by `get-threshold`; see [get-threshold](../directives/http.md#get-threshold) for observer root details), the next page is fetched and appended. Pagination stops automatically when the server returns an empty response.

### Directive Reference

| Attribute | Type | Description |
|-----------|------|-------------|
| `get-trigger` | `string` | How the next page is requested: `"scroll"` (IntersectionObserver-based), `"button"` (auto-generated "Load More"), or `"visible"` (fetch when element enters viewport) |
| `get-trigger` | `string` | How the next page is requested: `"scroll"` (IntersectionObserver-based), `"button"` (auto-generated "Load More"), or `"visible"` (fetch when element enters scroll container) |
| `get-trigger-label` | `string` | Label text for the load-more button (default: `"Load More"`) |
| `get-insert` | `string` | How new data is inserted: `"append"` (after existing) or `"prepend"` (before existing). **Required** for `scroll` and `button` triggers -- without it, content is replaced |
| `get-page` | `number` | Enable offset-based pagination. Sets the initial page number (default: `1`). Auto-increments on each fetch. Use `{page}` in the URL |
Expand Down