drop-downs
drop-down.js
Best way so far
// 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
}
Keyboard Navigation
keyboard-nav-letter-nums.js
BEST VERSION YETThis script allows quick keyboard-based navigation through visible focusable elements on the page (links and elements with an ID) using single letters or numbers.
Features:
- - Focus moves to elements whose text starts with the pressed letter or number.
- - Pressing the same key cycles forward or backward through matching elements (Shift+key for backward).
- - On a fresh key press, the closest matching element to the currently focused element is chosen.
- - Invisible elements or those with no size are automatically ignored.
- - The currently pressed key is tracked to manage cycling behavior.
Usage:
Include this script on a page to enhance keyboard accessibility and quick navigation. */
// keyboard-nav.js
addEventListener('keydown', e => {
// const activeEl = document.activeElement
const key = e.key.toLowerCase();
// Only single alphanumeric characters (letters + numbers)
if (key.length !== 1 || !/^[a-z0-9]$/.test(key)) return;
// Get all visible focusable elements
const focusableEls = [...document.querySelectorAll('a, [id]')].filter(el => {
const rect = el.getBoundingClientRect();
return el.offsetParent !== null && rect.width > 0 && rect.height > 0;
});
if (!focusableEls.length) return;
// Filter by first number OR first letter in the element text
const matchingEls = focusableEls.filter(el => {
const text = el.innerText.trim().toLowerCase();
const numMatch = text.match(/[0-9]/);
const letterMatch = text.match(/[a-z]/);
return (numMatch && numMatch[0] === key) ||
(letterMatch && letterMatch[0] === key);
});
if (matchingEls.length === 0) return;
const activeEl = document.activeElement;
let target;
if (window.lastLetterPressed === key) {
// Cycle within the same letter group
const iMatchingEls = matchingEls.indexOf(activeEl);
if (e.shiftKey) {
// backwards in the group
target = matchingEls[(iMatchingEls - 1 + matchingEls.length) % matchingEls.length];
} else {
// forwards in the group
target = matchingEls[(iMatchingEls + 1) % matchingEls.length];
}
} else {
// Fresh letter press → pick closest match (prefer below if equal)
const currentIndex = focusableEls.indexOf(activeEl);
const nextBelow = matchingEls.find(el => focusableEls.indexOf(el) > currentIndex);
const candidatesAbove = matchingEls.filter(el => focusableEls.indexOf(el) < currentIndex);
const nextAbove = candidatesAbove.length ? candidatesAbove[candidatesAbove.length - 1] : null;
if (nextBelow && nextAbove) {
const distBelow = focusableEls.indexOf(nextBelow) - currentIndex;
const distAbove = currentIndex - focusableEls.indexOf(nextAbove);
target = (distBelow <= distAbove) ? nextBelow : nextAbove;
} else {
target = nextBelow || nextAbove;
}
}
target?.focus();
window.lastLetterPressed = key;
});
Letter Focus Code
filter() and findIndex()
**make a dropCode like dropTopic so each code example can collapse... great job keep going,
you're doing great with implementing the new letterFocus incrementation !!😎
Chat Question: does filter() work the same as findIndex()?
ChatGPT said:
Awesome question filter is similar to findIndex, but with one big difference in what it returns.
✅ Both:
- * Take a callback function that checks a condition.
- * Go through the array element by element.
- * Use the callback to test each item.
🔍 Difference: Method Returns findIndex The index of the first matching item filter A new array of all matching items
const as = document.querySelectorAll('a')
let asTxtArr = [...as]
let lastPressedLetter = "";
let iLetter = 0;
function updateAsTxtArr(){
asTxtArr = Array.from(document.querySelectorAll('a'))
}
addEventListener('keydown', e => {
let letter = e.key.toLowerCase()
let letteredAs = asTxtArr.filter((el) => el.innerText.toLowerCase().startsWith(letter))
let activeEl = document.activeElement // current focused element
let activeIndex = [...asTxtArr].indexOf(activeEl)
if(letter != lastPressedLetter){
let closestIndex = letteredAs.findIndex((el) => asTxtArr.indexOf(el) > activeIndex)
iLetter = closestIndex !== -1 ? closestIndex : 0
} else {
iLetter = (letteredAs.indexOf(activeEl) + 1) % letteredAs.length
}
if(e.shiftKey){
iLetter = (letteredAs.indexOf(activeEl) - 1 + letteredAs.length) % letteredAs.length
}
if(letteredAs[iLetter]){
letteredAs[iLetter].focus()
}
lastPressedLetter = letter
})
CamelCase
function toCamelCase(str) {
return str
.toLowerCase()
.replace(/[^a-zA-Z0-9]+(.)/g, (match, chr) => chr.toUpperCase());
}
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());
}