♿ keyboard nav
This commit is contained in:
20
README.md
20
README.md
@@ -99,6 +99,7 @@ Don't have a server? No problem! Here are beginner-friendly options to get your
|
|||||||
|
|
||||||
- Comic viewer with navigation (First, Previous, Next, Latest)
|
- Comic viewer with navigation (First, Previous, Next, Latest)
|
||||||
- Client-side navigation using JSON API (no page reloads)
|
- Client-side navigation using JSON API (no page reloads)
|
||||||
|
- Keyboard navigation support (arrow keys, Home/End)
|
||||||
- Archive page with thumbnail grid
|
- Archive page with thumbnail grid
|
||||||
- RSS feed support
|
- RSS feed support
|
||||||
- Markdown support for author notes and about page
|
- Markdown support for author notes and about page
|
||||||
@@ -389,6 +390,25 @@ SOCIAL_YOUTUBE = 'https://youtube.com/@yourchannel'
|
|||||||
SOCIAL_EMAIL = 'mailto:your@email.com'
|
SOCIAL_EMAIL = 'mailto:your@email.com'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Navigation
|
||||||
|
|
||||||
|
Sunday Comics provides multiple ways for readers to navigate through your comic:
|
||||||
|
|
||||||
|
### Mouse/Touch Navigation
|
||||||
|
- **Navigation buttons**: First, Previous, Next, Latest buttons (text or icon-based)
|
||||||
|
- **Click-through**: Click on the comic image to advance to the next comic
|
||||||
|
- **Archive grid**: Click any thumbnail to jump directly to that comic
|
||||||
|
|
||||||
|
### Keyboard Navigation
|
||||||
|
When viewing a comic, readers can use keyboard shortcuts for quick navigation:
|
||||||
|
|
||||||
|
- **Left Arrow** (`←`) - Go to previous comic
|
||||||
|
- **Right Arrow** (`→`) - Go to next comic
|
||||||
|
- **Home** - Jump to first comic
|
||||||
|
- **End** - Jump to latest comic
|
||||||
|
|
||||||
|
Keyboard shortcuts respect navigation boundaries (won't navigate before the first comic or past the latest) and don't interfere with typing in input fields.
|
||||||
|
|
||||||
## Pages
|
## Pages
|
||||||
|
|
||||||
- `/` - Shows the latest comic
|
- `/` - Shows the latest comic
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
let totalComics = 0;
|
let totalComics = 0;
|
||||||
let comicName = ''; // Will be extracted from initial page title
|
let comicName = ''; // Will be extracted from initial page title
|
||||||
|
let currentComicNumber = 0;
|
||||||
|
|
||||||
// Fetch and display a comic
|
// Fetch and display a comic
|
||||||
async function loadComic(comicId) {
|
async function loadComic(comicId) {
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
// Update the page with comic data
|
// Update the page with comic data
|
||||||
function displayComic(comic) {
|
function displayComic(comic) {
|
||||||
const title = comic.title || `#${comic.number}`;
|
const title = comic.title || `#${comic.number}`;
|
||||||
|
currentComicNumber = comic.number;
|
||||||
|
|
||||||
// Update container class for full-width option
|
// Update container class for full-width option
|
||||||
const container = document.querySelector('.comic-container');
|
const container = document.querySelector('.comic-container');
|
||||||
@@ -227,6 +229,45 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle keyboard navigation
|
||||||
|
function handleKeyboardNavigation(event) {
|
||||||
|
// Don't interfere if user is typing in an input field
|
||||||
|
if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(event.key) {
|
||||||
|
case 'ArrowLeft':
|
||||||
|
// Previous comic
|
||||||
|
if (currentComicNumber > 1) {
|
||||||
|
event.preventDefault();
|
||||||
|
loadComic(currentComicNumber - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ArrowRight':
|
||||||
|
// Next comic
|
||||||
|
if (currentComicNumber < totalComics) {
|
||||||
|
event.preventDefault();
|
||||||
|
loadComic(currentComicNumber + 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Home':
|
||||||
|
// First comic
|
||||||
|
if (currentComicNumber > 1) {
|
||||||
|
event.preventDefault();
|
||||||
|
loadComic(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'End':
|
||||||
|
// Latest comic
|
||||||
|
if (currentComicNumber < totalComics) {
|
||||||
|
event.preventDefault();
|
||||||
|
loadComic(totalComics);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize on page load
|
// Initialize on page load
|
||||||
async function init() {
|
async function init() {
|
||||||
// Only run on comic pages
|
// Only run on comic pages
|
||||||
@@ -245,6 +286,7 @@
|
|||||||
|
|
||||||
// Get current comic number
|
// Get current comic number
|
||||||
const currentNumber = parseInt(document.querySelector('.comic-container').dataset.comicNumber || 0);
|
const currentNumber = parseInt(document.querySelector('.comic-container').dataset.comicNumber || 0);
|
||||||
|
currentComicNumber = currentNumber;
|
||||||
|
|
||||||
if (currentNumber && totalComics) {
|
if (currentNumber && totalComics) {
|
||||||
// Get the formatted date from the DOM (already rendered by server)
|
// Get the formatted date from the DOM (already rendered by server)
|
||||||
@@ -261,6 +303,9 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle keyboard navigation
|
||||||
|
document.addEventListener('keydown', handleKeyboardNavigation);
|
||||||
|
|
||||||
// Set initial state
|
// Set initial state
|
||||||
history.replaceState({ comicId: currentNumber }, '', window.location.pathname);
|
history.replaceState({ comicId: currentNumber }, '', window.location.pathname);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user