diff --git a/src/htmx.js b/src/htmx.js
index 7f444c11c..a9200b440 100644
--- a/src/htmx.js
+++ b/src/htmx.js
@@ -814,14 +814,14 @@ var htmx = (() => {
spec.observer = new IntersectionObserver((entries) => {
for (let i = 0; i < entries.length; i++) {
if (entries[i].isIntersecting) {
- this.trigger(elt, 'intersect', {}, false);
+ this.trigger(entries[i].target, 'intersect', {}, false);
if (isRevealed) spec.observer.disconnect();
break;
}
}
}, observerOptions);
eventName = 'intersect';
- spec.observer.observe(elt);
+ for (let fromElt of fromElts) spec.observer.observe(fromElt);
}
// Every: set up interval
diff --git a/test/tests/attributes/hx-on.js b/test/tests/attributes/hx-on.js
index cdc28b514..30c66ccac 100644
--- a/test/tests/attributes/hx-on.js
+++ b/test/tests/attributes/hx-on.js
@@ -583,7 +583,7 @@ describe('hx-on="eventSpec -> code" syntax', function() {
};
let div = createProcessedHTML('
x
');
div.classList.contains('visible').should.equal(false);
- observerCallback([{ isIntersecting: true }]);
+ observerCallback([{ isIntersecting: true, target: div }]);
div.classList.contains('visible').should.equal(true);
window.IntersectionObserver = originalIO;
});
@@ -597,7 +597,7 @@ describe('hx-on="eventSpec -> code" syntax', function() {
};
let div = createProcessedHTML('x
');
div.classList.contains('in-view').should.equal(false);
- observerCallback([{ isIntersecting: true }]);
+ observerCallback([{ isIntersecting: true, target: div }]);
div.classList.contains('in-view').should.equal(true);
window.IntersectionObserver = originalIO;
});
diff --git a/test/tests/unit/__initializeTriggers.js b/test/tests/unit/__initializeTriggers.js
index 4a207967c..28485a9a3 100644
--- a/test/tests/unit/__initializeTriggers.js
+++ b/test/tests/unit/__initializeTriggers.js
@@ -254,6 +254,30 @@ describe('__initializeTriggers unit tests', function() {
assert.isUndefined(captured[0].root);
assert.isUndefined(captured[0].threshold);
});
+
+ it('from: modifier observes the from element, not the trigger element', function() {
+ let observed = [];
+ const origIO = window.IntersectionObserver;
+ window.IntersectionObserver = function(cb, opts) {
+ const io = new origIO(cb, opts);
+ const origObserve = io.observe.bind(io);
+ io.observe = (el) => { observed.push(el); origObserve(el); };
+ return io;
+ };
+ playground().innerHTML = '';
+ htmx.process(playground());
+ window.IntersectionObserver = originalIO;
+ assert.equal(observed.length, 1);
+ assert.equal(observed[0], document.getElementById('scroller3'));
+ assert.notEqual(observed[0], document.getElementById('sentinel'));
+ });
+
+ it('from: modifier with threshold passes both to IntersectionObserver', function() {
+ playground().innerHTML = '';
+ htmx.process(playground());
+ assert.equal(captured.length, 1);
+ assert.equal(captured[0].threshold, 0.75);
+ });
});
});
\ No newline at end of file
diff --git a/www/src/content/reference/01-attributes/07-hx-trigger.md b/www/src/content/reference/01-attributes/07-hx-trigger.md
index 878986d3b..3995fc656 100644
--- a/www/src/content/reference/01-attributes/07-hx-trigger.md
+++ b/www/src/content/reference/01-attributes/07-hx-trigger.md
@@ -62,30 +62,27 @@ Fires when the element is loaded into the DOM. Useful for [lazy-loading](/patter
Loading...
```
-### `revealed`
+### `intersect` / `revealed`
-Fires when the element is scrolled into the viewport. Useful for [infinite scroll](/patterns/infinite-scroll).
+Both use the [IntersectionObserver API](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver#options) to fire when an element enters the viewport. The only difference is that `revealed` automatically disconnects the observer after the first intersection — it fires exactly once.
```html
+
+...
+
+
Loading...
```
-_Note: `revealed` always observes the browser viewport. For scrollable containers with `overflow`, use [`intersect`](#intersect) with `root` instead._
-
-### `intersect`
-
-Fires when an element becomes visible in the viewport.
-
-Uses the [IntersectionObserver API](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver#options).
-
-Modifiers:
+Modifiers (`intersect` and `revealed` both support these):
-- [`root`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/root)
-- [`rootMargin`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin)
-- [`threshold`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/thresholds)
+- `from:` — observe a different element instead of the element itself
+- [`root:`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/root) — use a scrollable container as the intersection root instead of the viewport
+- [`rootMargin:`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin) — expand or shrink the root bounds
+- [`threshold:<0–1>`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/thresholds) — minimum visible fraction before firing
```html
-...
+...
...
...
...
diff --git a/www/src/content/reference/03-events/33-intersect.md b/www/src/content/reference/03-events/33-intersect.md
index dafdab938..b0fdf74fc 100644
--- a/www/src/content/reference/03-events/33-intersect.md
+++ b/www/src/content/reference/03-events/33-intersect.md
@@ -3,11 +3,13 @@ title: "intersect"
description: "Fires when element enters viewport"
---
-The `intersect` trigger event fires when an element enters the viewport, detected via IntersectionObserver.
+The `intersect` event fires when an observed element enters the viewport, detected via IntersectionObserver. It is dispatched on the observed element.
+
+`revealed` is identical except it disconnects the observer after the first intersection, so it fires exactly once.
## When It Fires
-When an element becomes visible in the viewport, used by [`hx-trigger`](/reference/attributes/hx-trigger)`="intersect"`.
+Used by [`hx-trigger`](/reference/attributes/hx-trigger)`="intersect"` and `hx-trigger="revealed"`. Also fires when `from:` is used on either — the event is dispatched on the `from` element when it intersects.
## Event Detail
@@ -16,9 +18,14 @@ Empty - no additional context provided.
## Example
```html
-
- Content loads when scrolled into view
-
+
+...
+
+
+Loading...
+
+
+...
```
```javascript
@@ -26,5 +33,3 @@ htmx.on('#lazy', 'intersect', (evt) => {
console.log('Element visible:', evt.target);
});
```
-
-This enables lazy loading and infinite scroll patterns.