Keyboard Navigation
keyboard-nav.js BEST VERSION YET
// keyboard-nav.js
let lastLetterPressed = null
import { pageWrapper,sideBarBtn } from "../ui/toggle-sidebar.js"
export function keyboardNav({ e, mainContentEls }) {
const key = (e.key || '').toLowerCase()
if (!key.match(/^[a-z]$/)) return // only handle letters
// all visible anchors (same as you had)
if(e.metaKey && e.shiftKey && key === 's'){sideBarBtn.focus()}
// const allEls = [...document.querySelectorAll('a,#sideBarBtn,#mainLandingPage,#darkModeBtn,#findSearchBar')].filter(el => {
// // // // // // // // // // // // // // // // // // //
//** make command shift + f , focus to #findSearchBar */
const allEls = [...document.querySelectorAll('a,#sideBarBtn,#mainContent,#darkModeBtn,#chatGptMyLink,#programShortcutsLink')].filter(el => {
const rect = el.getBoundingClientRect()
if(!el.hasAttribute('tabindex')){
el.setAttribute('tabindex', '0')
}
return isActuallyVisible(el)
})
// helper: return the first alphabetic character of the element's text (or '')
const firstAlpha = el => {
// If element is NOT an anchor, use its ID
if (el.tagName !== 'A') {
const id = (el.id || '').trim().toLowerCase()
for (let i = 0; i < id.length; i++) {
const ch = id[i]
if (/[a-z]/.test(ch)) return ch
}
return ''
}
// Regular text logic
const s = (el.innerText || '').trim().toLowerCase()
for (let i = 0; i < s.length; i++) {
if (/[a-z]/.test(s[i])) return s[i]
}
return ''
}
// matching anchors whose first alpha char equals the pressed key
// const matching = allEls.filter(el => firstAlpha(el) === key)
const matching = allEls.filter(el =>{
return firstAlpha(el) === key
})
// matching.forEach(el => console.log(el))
if (matching.length === 0) return
const activeEl = document.activeElement
let iActiveAll = allEls.indexOf(activeEl) // position of focused element among all anchors
const iActiveMatching = matching.indexOf(activeEl) // -1 if focused element is not one of the matches
let newIndex
if (key === 'm' && activeEl?.id === 'mainLandingPage') {
e.preventDefault()
// topicsContainer.scrollIntoView({ top: 0, behavior: 'smooth' })
lastLetterPressed = key
return
}
if(e.metaKey) return
// --- NEW letter press: choose closest match below unless one is directly before (closer) ---
if (key !== lastLetterPressed) {
if (iActiveAll === -1) {
// nothing focused: pick first/last
newIndex = e.shiftKey ? matching.length - 1 : 0
} else {
const prevEl = allEls[iActiveAll - 1] // the element directly before
const nextEl = allEls[iActiveAll + 1] // the element directly after
// if the previous element matches the letter, go up one
if (prevEl && matching.includes(prevEl)) {
newIndex = matching.indexOf(prevEl)
} else {
// otherwise go to the next matching element after current focus
let foundNext = false
for (let i = iActiveAll + 1; i < allEls.length; i++) {
if (matching.includes(allEls[i])) {
newIndex = matching.indexOf(allEls[i])
foundNext = true
break
}
}
if (!foundNext) {
// fallback to first matching if nothing found below
newIndex = 0
}
}
}
}else {
if (iActiveMatching === -1) {
// currently focused element is not one of the matching elements
newIndex = e.shiftKey ? matching.length - 1 : 0
} else {
newIndex = e.shiftKey
? (iActiveMatching - 1 + matching.length) % matching.length
: (iActiveMatching + 1) % matching.length
}
}
let target = matching[newIndex]
if (!target) return
lastLetterPressed = key
// let fZone = focusZones(target)
// if (target == sideBarBtn) {
// fZone = 'sidebar'
// pageWrapper.classList.remove('collapsed')
// }
// if(e.shiftKey && !e.target.classList.contains('copy-code')){
// console.log(allEls[iActiveAll].innerText[0])
// console.log()
// target.focus()
// if (key == allEls[iActiveAll].innerText[0].toLowerCase()){
// console.log('here', iActiveAll)
// iActiveAll += 1
// }
// target = allEls[iActiveAll]
// }
target.focus()
}
// function focusZones(target){
// const t = target
// let focusZone
// if (t.id == 'mainContent'){
// // focusZone = 'mainContent'
// } else if (t.closest('.side-bar') ){
// // focusZone = 'sidmeBar'
// }
// // switch (t){
// // case target.id === 'mainContent':
// // return focusZone = 'mainContent'
// // break
// // case target.
// // }
// return focusZone
// }
function isActuallyVisible(el) {
if (!el) return false;
// 1. Sidebar collapsed → block ALL sidebar descendants
if (
pageWrapper.classList.contains('collapsed') &&
el.closest('.side-bar')
) {
return false;
}
// 2. CSS visibility checks
const style = getComputedStyle(el);
if (
style.display === 'none' ||
style.visibility === 'hidden' ||
style.opacity === '0'
) {
return false;
}
// 3. Zero-size or clipped
const rect = el.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) {
return false;
}
// 4. Any hidden ancestor (dropdowns, containers, etc.)
let parent = el.parentElement;
while (parent) {
const ps = getComputedStyle(parent);
if (ps.display === 'none' || ps.visibility === 'hidden') {
return false;
}
parent = parent.parentElement;
}
return true;
}
letter-focus.js find & add good one
Add Good letter focus here
drop-downs
drop-down.js not the best( best is used on this page)
// drop-downs.js
export function initDropDowns() {
hideAllCodeCmds()
if(!document.listenersAdded){
document.addEventListener("click", handleToggle);
document.addEventListener("keydown", handleToggle);
document.listenersAdded = true
}
// const dropChilds = document.querySelectorAll('.code-cmd') ? document.querySelectorAll('.code-cmd') : document.querySelectorAll('.topic-snips')
function handleToggle(e) {
let target;
if (e.type === "keydown") {
if ((e.key === "Enter" || e.key === " ") && (document.activeElement.classList.contains("drop-down") || document.activeElement.classList.contains("drop-code-cmd"))) {
e.preventDefault();
target = document.activeElement;
} else {
return; // ignore other keys
}
} else if (e.type === "click") {
// Ignore clicks triggered by keyboard
if (e.detail === 0) return;
target = e.target.closest(".drop-down") || e.target.closest(".drop-code-cmd");
if (!target) return;
}
// Unified toggle logic
const topic = target.closest(".topic");
const snip = target.closest(".snip");
// console.log("Toggled dropdown:", topic);
// if (!topic || !snip) return;
console.log(snip)
if (snip){
toggleCodeSnips(snip)
return
}
if(topic){
toggleTopicSnips(topic)
return
}
}
}
function toggleCodeSnips(snip) {
const codeCmd = snip.querySelector('.code-cmd')
codeCmd.classList.toggle('hide')
}
function hideAllCodeCmds() {
const codeCmds = document.querySelectorAll('.code-cmd')
codeCmds.forEach(el => {
if(!el.classList.contains('show')){
el.classList.add('hide')
}
})
}
function toggleTopicSnips(topic) {
const topicSnips = topic.querySelector('.topic-snips')
topicSnips.classList.toggle("hide"); // example toggle
}
Drop
2
// This page has really good letter focus cycle
if(letter == 'c' && !e.metaKey){
if(!e.shiftKey){
copyCodes[iCopyCodes].focus()
iCopyCodes = (iCopyCodes + 1) % copyCodes.length
} else{
iCopyCodes = (iCopyCodes - 1 + copyCodes.length) % copyCodes.length
}
copyCodes[iCopyCodes].focus()
console.log(iLetter)
}
CamelCase function
function toCamelCase(str) {
return str
.toLowerCase()
.replace(/[^a-zA-Z0-9]+(.)/g, (match, chr) => chr.toUpperCase());
}
Drop downs
Incrementing / Decrements Letter Focus
function hideTopics(){
}
CamelCase function
function toCamelCase(str) {
return str
.toLowerCase()
.replace(/[^a-zA-Z0-9]+(.)/g, (match, chr) => chr.toUpperCase());
}