mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
Add keyboard navigation to more pages
- Books page: 2D grid navigation (up/down moves by row) - Fruits of Spirit: entry navigation - Tetragrammaton: section navigation - Verse of the Day: archive table navigation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -304,11 +304,20 @@
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const cards = document.querySelectorAll('.book-card');
|
||||
const cards = Array.from(document.querySelectorAll('.book-card'));
|
||||
if (cards.length === 0) return;
|
||||
|
||||
let selectedIndex = -1;
|
||||
|
||||
function getGridColumns() {
|
||||
// Detect columns based on viewport and grid settings
|
||||
const grid = document.querySelector('.book-grid');
|
||||
if (!grid) return 1;
|
||||
const style = getComputedStyle(grid);
|
||||
const cols = style.gridTemplateColumns.split(' ').length;
|
||||
return cols || 1;
|
||||
}
|
||||
|
||||
function selectCard(index) {
|
||||
// Remove previous selection
|
||||
if (selectedIndex >= 0 && selectedIndex < cards.length) {
|
||||
@@ -332,12 +341,36 @@
|
||||
// Don't handle if user is typing in an input
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
||||
|
||||
const cols = getGridColumns();
|
||||
|
||||
if (e.key === 'ArrowDown' || e.key === 'j') {
|
||||
e.preventDefault();
|
||||
selectCard(selectedIndex + 1);
|
||||
if (selectedIndex < 0) {
|
||||
selectCard(0);
|
||||
} else {
|
||||
selectCard(selectedIndex + cols);
|
||||
}
|
||||
} else if (e.key === 'ArrowUp' || e.key === 'k') {
|
||||
e.preventDefault();
|
||||
selectCard(selectedIndex - 1);
|
||||
if (selectedIndex < 0) {
|
||||
selectCard(0);
|
||||
} else if (selectedIndex - cols >= 0) {
|
||||
selectCard(selectedIndex - cols);
|
||||
}
|
||||
} else if (e.key === 'ArrowRight' || e.key === 'l') {
|
||||
e.preventDefault();
|
||||
if (selectedIndex < 0) {
|
||||
selectCard(0);
|
||||
} else {
|
||||
selectCard(selectedIndex + 1);
|
||||
}
|
||||
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
|
||||
e.preventDefault();
|
||||
if (selectedIndex <= 0) {
|
||||
history.back();
|
||||
} else {
|
||||
selectCard(selectedIndex - 1);
|
||||
}
|
||||
} else if (e.key === 'Enter' && selectedIndex >= 0) {
|
||||
e.preventDefault();
|
||||
window.location.href = cards[selectedIndex].href;
|
||||
|
||||
@@ -274,6 +274,43 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
li.appendChild(a);
|
||||
tocList.appendChild(li);
|
||||
});
|
||||
|
||||
// Keyboard navigation
|
||||
const entries = Array.from(document.querySelectorAll('.fruit-entry'));
|
||||
if (entries.length === 0) return;
|
||||
|
||||
let selectedIndex = -1;
|
||||
|
||||
function selectEntry(index) {
|
||||
if (selectedIndex >= 0 && selectedIndex < entries.length) {
|
||||
entries[selectedIndex].style.outline = '';
|
||||
entries[selectedIndex].style.outlineOffset = '';
|
||||
}
|
||||
selectedIndex = Math.max(0, Math.min(index, entries.length - 1));
|
||||
entries[selectedIndex].style.outline = '2px solid #4a7c59';
|
||||
entries[selectedIndex].style.outlineOffset = '4px';
|
||||
entries[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
||||
|
||||
if (e.key === 'ArrowDown' || e.key === 'j') {
|
||||
e.preventDefault();
|
||||
selectEntry(selectedIndex < 0 ? 0 : selectedIndex + 1);
|
||||
} else if (e.key === 'ArrowUp' || e.key === 'k') {
|
||||
e.preventDefault();
|
||||
if (selectedIndex <= 0) selectEntry(0);
|
||||
else selectEntry(selectedIndex - 1);
|
||||
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
|
||||
e.preventDefault();
|
||||
history.back();
|
||||
} else if (e.key === 'Enter' && selectedIndex >= 0) {
|
||||
e.preventDefault();
|
||||
const link = entries[selectedIndex].querySelector('.fruit-name a');
|
||||
if (link) window.location.href = link.href;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -151,6 +151,39 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
li.appendChild(a);
|
||||
tocList.appendChild(li);
|
||||
});
|
||||
|
||||
// Keyboard navigation for sections
|
||||
const sections = Array.from(document.querySelectorAll('section h2')).map(h => h.closest('section'));
|
||||
if (sections.length === 0) return;
|
||||
|
||||
let selectedIndex = -1;
|
||||
|
||||
function selectSection(index) {
|
||||
if (selectedIndex >= 0 && selectedIndex < sections.length) {
|
||||
sections[selectedIndex].style.outline = '';
|
||||
sections[selectedIndex].style.outlineOffset = '';
|
||||
}
|
||||
selectedIndex = Math.max(0, Math.min(index, sections.length - 1));
|
||||
sections[selectedIndex].style.outline = '2px solid #4a7c59';
|
||||
sections[selectedIndex].style.outlineOffset = '4px';
|
||||
sections[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
||||
|
||||
if (e.key === 'ArrowDown' || e.key === 'j') {
|
||||
e.preventDefault();
|
||||
selectSection(selectedIndex < 0 ? 0 : selectedIndex + 1);
|
||||
} else if (e.key === 'ArrowUp' || e.key === 'k') {
|
||||
e.preventDefault();
|
||||
if (selectedIndex <= 0) selectSection(0);
|
||||
else selectSection(selectedIndex - 1);
|
||||
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
|
||||
e.preventDefault();
|
||||
history.back();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -46,4 +46,44 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const rows = Array.from(document.querySelectorAll('tbody tr'));
|
||||
if (rows.length === 0) return;
|
||||
|
||||
let selectedIndex = -1;
|
||||
|
||||
function selectRow(index) {
|
||||
if (selectedIndex >= 0 && selectedIndex < rows.length) {
|
||||
rows[selectedIndex].style.outline = '';
|
||||
rows[selectedIndex].style.outlineOffset = '';
|
||||
}
|
||||
selectedIndex = Math.max(0, Math.min(index, rows.length - 1));
|
||||
rows[selectedIndex].style.outline = '2px solid #4a7c59';
|
||||
rows[selectedIndex].style.outlineOffset = '2px';
|
||||
rows[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
||||
|
||||
if (e.key === 'ArrowDown' || e.key === 'j') {
|
||||
e.preventDefault();
|
||||
selectRow(selectedIndex < 0 ? 0 : selectedIndex + 1);
|
||||
} else if (e.key === 'ArrowUp' || e.key === 'k') {
|
||||
e.preventDefault();
|
||||
if (selectedIndex <= 0) selectRow(0);
|
||||
else selectRow(selectedIndex - 1);
|
||||
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
|
||||
e.preventDefault();
|
||||
history.back();
|
||||
} else if (e.key === 'Enter' && selectedIndex >= 0) {
|
||||
e.preventDefault();
|
||||
const link = rows[selectedIndex].querySelector('a');
|
||||
if (link) window.location.href = link.href;
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user