mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
Add Strong's tooltip on hover for word study links
Hovering over the Greek/Hebrew term in word study sidenotes now shows a tooltip with the Strong's card: original word, transliteration, Strong's number, and definition. Uses same positioning logic as verse tooltips. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -160,7 +160,7 @@ def inject_word_markers(text, word_studies, verse_num):
|
||||
# Link the original term to Strong's page if we have a Strong's number
|
||||
strongs = study.get('strongs')
|
||||
if strongs:
|
||||
term_html = f'<a href="/strongs/{strongs}">{study["term"]}</a>'
|
||||
term_html = f'<a href="/strongs/{strongs}" class="strongs-link" data-strongs="{strongs}">{study["term"]}</a>'
|
||||
else:
|
||||
term_html = study["term"]
|
||||
# Wrap details (translit + note) in a span that's hidden by default
|
||||
|
||||
@@ -1619,6 +1619,113 @@ function showKeyboardHelp() {
|
||||
});
|
||||
})();
|
||||
|
||||
// Strong's tooltip functionality
|
||||
(function() {
|
||||
// Create tooltip element
|
||||
var tooltip = document.createElement('div');
|
||||
tooltip.className = 'strongs-tooltip';
|
||||
document.body.appendChild(tooltip);
|
||||
|
||||
// Cache for fetched Strong's entries
|
||||
var strongsCache = {};
|
||||
|
||||
// Fetch Strong's entry from API
|
||||
async function fetchStrongsEntry(strongsNumber) {
|
||||
if (strongsCache[strongsNumber]) {
|
||||
return strongsCache[strongsNumber];
|
||||
}
|
||||
|
||||
try {
|
||||
var response = await fetch('/api/strongs/' + strongsNumber);
|
||||
if (!response.ok) {
|
||||
throw new Error('Not found');
|
||||
}
|
||||
var data = await response.json();
|
||||
strongsCache[strongsNumber] = data;
|
||||
return data;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Show tooltip positioned relative to a link element
|
||||
function showTooltip(data, linkElement) {
|
||||
var isHebrew = data.strongs.startsWith('H');
|
||||
var langClass = isHebrew ? 'hebrew' : 'greek';
|
||||
|
||||
tooltip.innerHTML =
|
||||
'<div class="strongs-tooltip-word ' + langClass + '">' + data.word + '</div>' +
|
||||
'<div class="strongs-tooltip-translit">' + data.transliteration + '</div>' +
|
||||
'<div class="strongs-tooltip-number">' + data.strongs + '</div>' +
|
||||
'<div class="strongs-tooltip-def">' + data.definition + '</div>';
|
||||
|
||||
// Position off-screen first to measure
|
||||
tooltip.style.left = '-9999px';
|
||||
tooltip.style.top = '-9999px';
|
||||
tooltip.classList.add('show');
|
||||
|
||||
var tooltipWidth = tooltip.offsetWidth;
|
||||
var tooltipHeight = tooltip.offsetHeight;
|
||||
|
||||
var linkRect = linkElement.getBoundingClientRect();
|
||||
var padding = 10;
|
||||
|
||||
// Position below the link, centered
|
||||
var x = linkRect.left + (linkRect.width / 2) - (tooltipWidth / 2);
|
||||
var y = linkRect.bottom + 8 + window.scrollY;
|
||||
|
||||
// Keep within viewport
|
||||
if (x + tooltipWidth > window.innerWidth - padding) {
|
||||
x = window.innerWidth - tooltipWidth - padding;
|
||||
}
|
||||
if (x < padding) {
|
||||
x = padding;
|
||||
}
|
||||
if (linkRect.bottom + 8 + tooltipHeight > window.innerHeight) {
|
||||
y = linkRect.top - tooltipHeight - 8 + window.scrollY;
|
||||
}
|
||||
if (y < window.scrollY + padding) {
|
||||
y = window.scrollY + padding;
|
||||
}
|
||||
|
||||
tooltip.style.left = x + 'px';
|
||||
tooltip.style.top = y + 'px';
|
||||
}
|
||||
|
||||
function hideTooltip() {
|
||||
tooltip.classList.remove('show');
|
||||
}
|
||||
|
||||
// Event delegation for Strong's links
|
||||
document.addEventListener('mouseover', function(e) {
|
||||
var target = e.target;
|
||||
if (!target.classList.contains('strongs-link')) return;
|
||||
|
||||
var strongsNumber = target.getAttribute('data-strongs');
|
||||
if (!strongsNumber) return;
|
||||
|
||||
// Show loading state
|
||||
tooltip.innerHTML = '<span style="color: var(--text-tertiary);">Loading...</span>';
|
||||
var linkRect = target.getBoundingClientRect();
|
||||
tooltip.style.left = linkRect.left + 'px';
|
||||
tooltip.style.top = (linkRect.bottom + 8 + window.scrollY) + 'px';
|
||||
tooltip.classList.add('show');
|
||||
|
||||
// Fetch and display
|
||||
fetchStrongsEntry(strongsNumber).then(function(data) {
|
||||
if (data && target.matches(':hover')) {
|
||||
showTooltip(data, target);
|
||||
} else if (!data) {
|
||||
hideTooltip();
|
||||
}
|
||||
});
|
||||
|
||||
target.addEventListener('mouseleave', function() {
|
||||
hideTooltip();
|
||||
}, { once: true });
|
||||
});
|
||||
})();
|
||||
|
||||
// Site-wide verse linking
|
||||
(function() {
|
||||
function linkVerseReferences(element) {
|
||||
|
||||
@@ -301,6 +301,76 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Strong's tooltip styles */
|
||||
.strongs-tooltip {
|
||||
position: absolute;
|
||||
background: var(--bg-color);
|
||||
border: 1px solid var(--border-color-darker);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem 1rem;
|
||||
min-width: 250px;
|
||||
max-width: 350px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
z-index: 9999;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.5;
|
||||
color: var(--text-color);
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.strongs-tooltip.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.strongs-tooltip-word {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.strongs-tooltip-word.hebrew {
|
||||
font-family: 'SBL Hebrew', 'Ezra SIL', 'Times New Roman', serif;
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.strongs-tooltip-word.greek {
|
||||
font-family: 'SBL Greek', 'Gentium Plus', 'Times New Roman', serif;
|
||||
}
|
||||
|
||||
.strongs-tooltip-translit {
|
||||
font-style: italic;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.strongs-tooltip-number {
|
||||
font-family: monospace;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-tertiary);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.strongs-tooltip-def {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .strongs-tooltip {
|
||||
background: #2a2a2a;
|
||||
border-color: #444;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html:not([data-theme="light"]) .strongs-tooltip {
|
||||
background: #2a2a2a;
|
||||
border-color: #444;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enhanced typography and spacing */
|
||||
body {
|
||||
counter-reset: sidenote-counter;
|
||||
|
||||
Reference in New Issue
Block a user