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 = '
Test
'; + 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 = '
Test
'; + 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.