mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
Add TOC drill-down keyboard navigation on resource index pages
- TOC is now a selectable block that can be navigated to with arrow keys - Press Enter or Right arrow to drill into individual TOC entries - Navigate TOC entries with Up/Down, Enter or Right to follow link - Left arrow exits TOC items back to TOC block 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -281,6 +281,7 @@
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const tocList = document.getElementById('toc-list');
|
||||
const toc = document.getElementById('toc');
|
||||
const headings = document.querySelectorAll('section h2, section h3, section article h3');
|
||||
|
||||
headings.forEach((heading, index) => {
|
||||
@@ -298,21 +299,59 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
tocList.appendChild(li);
|
||||
});
|
||||
|
||||
// Keyboard navigation for intro, description paragraphs, and verses
|
||||
const elements = Array.from(document.querySelectorAll('.intro-text, .resource-item-description p, .verse-item'));
|
||||
if (elements.length === 0) return;
|
||||
// Two-section navigation: TOC block -> content elements
|
||||
// Or when inside TOC: individual TOC entries
|
||||
const contentElements = Array.from(document.querySelectorAll('.intro-text, .resource-item-description p, .verse-item'));
|
||||
const tocItems = Array.from(tocList.querySelectorAll('li a'));
|
||||
|
||||
let currentSection = 'content'; // 'content' or 'toc'
|
||||
let selectedIndex = -1;
|
||||
let selectedTocIndex = -1;
|
||||
let tocBlockSelected = false;
|
||||
|
||||
function selectElement(index) {
|
||||
if (selectedIndex >= 0 && selectedIndex < elements.length) {
|
||||
elements[selectedIndex].style.outline = '';
|
||||
elements[selectedIndex].style.outlineOffset = '';
|
||||
function clearAllSelections() {
|
||||
if (selectedIndex >= 0 && selectedIndex < contentElements.length) {
|
||||
contentElements[selectedIndex].style.outline = '';
|
||||
contentElements[selectedIndex].style.outlineOffset = '';
|
||||
}
|
||||
selectedIndex = Math.max(0, Math.min(index, elements.length - 1));
|
||||
elements[selectedIndex].style.outline = '2px solid #4a7c59';
|
||||
elements[selectedIndex].style.outlineOffset = '8px';
|
||||
elements[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
if (tocBlockSelected) {
|
||||
toc.style.outline = '';
|
||||
toc.style.outlineOffset = '';
|
||||
tocBlockSelected = false;
|
||||
}
|
||||
tocItems.forEach(item => {
|
||||
item.style.outline = '';
|
||||
item.style.background = '';
|
||||
});
|
||||
}
|
||||
|
||||
function selectTocBlock() {
|
||||
clearAllSelections();
|
||||
currentSection = 'toc';
|
||||
tocBlockSelected = true;
|
||||
selectedTocIndex = -1;
|
||||
toc.style.outline = '2px solid #4a7c59';
|
||||
toc.style.outlineOffset = '8px';
|
||||
toc.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
|
||||
function selectTocItem(index) {
|
||||
clearAllSelections();
|
||||
currentSection = 'toc';
|
||||
tocBlockSelected = false;
|
||||
selectedTocIndex = Math.max(0, Math.min(index, tocItems.length - 1));
|
||||
tocItems[selectedTocIndex].style.background = 'rgba(74, 124, 89, 0.15)';
|
||||
tocItems[selectedTocIndex].style.outline = '2px solid #4a7c59';
|
||||
tocItems[selectedTocIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
|
||||
function selectContentElement(index) {
|
||||
clearAllSelections();
|
||||
currentSection = 'content';
|
||||
selectedIndex = Math.max(0, Math.min(index, contentElements.length - 1));
|
||||
contentElements[selectedIndex].style.outline = '2px solid #4a7c59';
|
||||
contentElements[selectedIndex].style.outlineOffset = '8px';
|
||||
contentElements[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
@@ -320,27 +359,76 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
if (e.key === 'ArrowDown' || e.key === 'j') {
|
||||
e.preventDefault();
|
||||
selectElement(selectedIndex < 0 ? 0 : selectedIndex + 1);
|
||||
if (currentSection === 'toc') {
|
||||
if (tocBlockSelected) {
|
||||
// Move from TOC block to first content element
|
||||
selectContentElement(0);
|
||||
} else {
|
||||
// Navigate within TOC items
|
||||
selectTocItem(selectedTocIndex + 1);
|
||||
}
|
||||
} else {
|
||||
// Navigate content elements
|
||||
selectContentElement(selectedIndex < 0 ? 0 : selectedIndex + 1);
|
||||
}
|
||||
} else if (e.key === 'ArrowUp' || e.key === 'k') {
|
||||
e.preventDefault();
|
||||
if (selectedIndex <= 0) selectElement(0);
|
||||
else selectElement(selectedIndex - 1);
|
||||
if (currentSection === 'toc') {
|
||||
if (tocBlockSelected) {
|
||||
// Already at TOC block, stay there
|
||||
selectTocBlock();
|
||||
} else if (selectedTocIndex <= 0) {
|
||||
// At first TOC item, go back to TOC block
|
||||
selectTocBlock();
|
||||
} else {
|
||||
selectTocItem(selectedTocIndex - 1);
|
||||
}
|
||||
} else {
|
||||
if (selectedIndex <= 0) {
|
||||
// At first content element, go to TOC block
|
||||
selectTocBlock();
|
||||
} else {
|
||||
selectContentElement(selectedIndex - 1);
|
||||
}
|
||||
}
|
||||
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
|
||||
e.preventDefault();
|
||||
history.back();
|
||||
} else if (e.key === 'Enter' && selectedIndex >= 0) {
|
||||
if (currentSection === 'toc' && !tocBlockSelected) {
|
||||
// Exit TOC items back to TOC block
|
||||
selectTocBlock();
|
||||
} else {
|
||||
history.back();
|
||||
}
|
||||
} else if (e.key === 'ArrowRight' || e.key === 'l') {
|
||||
e.preventDefault();
|
||||
var el = elements[selectedIndex];
|
||||
// Check for link in resource entry title or verse reference
|
||||
var link = el.querySelector('.resource-name a') || el.querySelector('.verse-ref a') || el.querySelector('a');
|
||||
if (link) window.location.href = link.href;
|
||||
if (tocBlockSelected) {
|
||||
// Drill into TOC items
|
||||
selectTocItem(0);
|
||||
} else if (currentSection === 'toc' && selectedTocIndex >= 0) {
|
||||
// Navigate to the linked section
|
||||
var href = tocItems[selectedTocIndex].getAttribute('href');
|
||||
if (href) window.location.href = href;
|
||||
}
|
||||
} else if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if (tocBlockSelected) {
|
||||
// Drill into TOC items
|
||||
selectTocItem(0);
|
||||
} else if (currentSection === 'toc' && selectedTocIndex >= 0) {
|
||||
// Navigate to the linked section
|
||||
var href = tocItems[selectedTocIndex].getAttribute('href');
|
||||
if (href) window.location.href = href;
|
||||
} else if (selectedIndex >= 0) {
|
||||
var el = contentElements[selectedIndex];
|
||||
var link = el.querySelector('.resource-name a') || el.querySelector('.verse-ref a') || el.querySelector('a');
|
||||
if (link) window.location.href = link.href;
|
||||
}
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
if (selectedIndex >= 0 && selectedIndex < elements.length) {
|
||||
elements[selectedIndex].style.outline = '';
|
||||
elements[selectedIndex].style.outlineOffset = '';
|
||||
}
|
||||
clearAllSelections();
|
||||
currentSection = 'content';
|
||||
selectedIndex = -1;
|
||||
selectedTocIndex = -1;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user