# Infinite scroll without breaking the back button
## Auto-Load Infinite Scroll (Progressive Enhancement)
## Overview
The "Load More" button now has a stable ID (`load-more-trigger`) that enables progressive enhancement with auto-loading via IntersectionObserver.
## Current Implementation
The button is accessible via:
```html
Load More Posts
```
## Progressive Enhancement: Auto-Load on Scroll
You can optionally add JavaScript to automatically load more posts when the user scrolls near the button. This is **completely optional** - the button works perfectly without it.
### Implementation
**File**: `app/javascript/application.js` (or your main JS file)
```javascript
// Auto-load infinite scroll when "Load More" button becomes visible
document.addEventListener('turbo:load', () => {
const loadMoreButton = document.getElementById('load-more-trigger');
if (!loadMoreButton) return;
// Create IntersectionObserver to watch when button enters viewport
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
// When button is visible (threshold: 10% visible)
if (entry.isIntersecting) {
// Click the button to load more posts
loadMoreButton.click();
}
});
}, {
threshold: 0.1, // Trigger when 10% of button is visible
rootMargin: '200px' // Start loading 200px before button is visible
});
// Start observing the button
observer.observe(loadMoreButton);
});
```
### How It Works
1. **User scrolls down** the blog posts page
2. **IntersectionObserver detects** when "Load More" button enters viewport
3. **Automatically clicks** the button (triggers Turbo Frame request)
4. **New posts load** via Turbo Stream (same as manual click)
5. **Process repeats** until all posts are loaded
### Benefits
✅ **Progressive Enhancement**: Works without JS, enhanced with JS
✅ **Better UX**: No need to click "Load More" manually
✅ **Stable ID**: Button always has `id="load-more-trigger"`
✅ **Turbo Compatible**: Uses Turbo events (`turbo:load`)
✅ **Performance**: Only loads when user scrolls near bottom
### Configuration Options
```javascript
{
threshold: 0.1, // Trigger when 10% visible (0.0 to 1.0)
rootMargin: '200px' // Start loading 200px before visible
}
```
**Adjust based on your needs:**
- **Higher threshold** (e.g., `0.5`) = Load when button is 50% visible
- **Larger rootMargin** (e.g., `'500px'`) = Start loading earlier
- **Lower threshold** (e.g., `0.05`) = Load when button is barely visible
### Loading Indicator (Optional)
You can add a loading state:
```javascript
document.addEventListener('turbo:load', () => {
const loadMoreButton = document.getElementById('load-more-trigger');
if (!loadMoreButton) return;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Add loading state
loadMoreButton.disabled = true;
loadMoreButton.innerHTML = 'Loading...';
// Click to load
loadMoreButton.click();
}
});
}, { threshold: 0.1, rootMargin: '200px' });
observer.observe(loadMoreButton);
// Re-enable button after Turbo Stream completes
document.addEventListener('turbo:frame-load', () => {
const button = document.getElementById('load-more-trigger');
if (button) {
button.disabled = false;
button.innerHTML = 'Load More Posts';
}
});
});
```
### Error Handling (Optional)
```javascript
document.addEventListener('turbo:load', () => {
const loadMoreButton = document.getElementById('load-more-trigger');
if (!loadMoreButton) return;
let isLoading = false;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !isLoading) {
isLoading = true;
loadMoreButton.click();
}
});
}, { threshold: 0.1, rootMargin: '200px' });
observer.observe(loadMoreButton);
// Reset loading state after frame loads
document.addEventListener('turbo:frame-load', () => {
isLoading = false;
});
// Handle errors
document.addEventListener('turbo:frame-missing', () => {
isLoading = false;
console.error('Failed to load more posts');
});
});
```
## Browser Compatibility
IntersectionObserver is supported in:
- ✅ Chrome 51+
- ✅ Firefox 55+
- ✅ Safari 12.1+
- ✅ Edge 15+
For older browsers, you can use a polyfill or fall back to manual clicking.
## Summary
The stable `id="load-more-trigger"` enables:
- ✅ **Auto-loading** via IntersectionObserver
- ✅ **Progressive enhancement** (works without JS)
- ✅ **Future enhancements** (loading states, error handling, etc.)
- ✅ **Consistent behavior** across page loads and Turbo Stream updates
**Note**: Auto-loading is **optional**. The infinite scroll works perfectly without JavaScript - users can manually click "Load More" if they prefer.