search-with-your-keyboard/index.js

100 строки
2.8 KiB
JavaScript
Исходник Обычный вид История

2018-06-01 22:22:39 +03:00
const keycode = require('keycode')
const assert = require('assert')
2018-06-03 22:44:48 +03:00
module.exports = function searchWithYourKeyboard (inputSelector, hitsSelector) {
2018-06-01 22:22:39 +03:00
assert(typeof inputSelector === 'string', 'inputSelector should be a string')
2018-06-03 22:44:48 +03:00
assert(typeof hitsSelector === 'string', 'hitsSelector should be a string')
2018-06-01 22:22:39 +03:00
let activeIndex = 0
const targetEventCodes = ['up', 'down', 'enter', '/', 'esc']
const input = document.querySelector(inputSelector)
// deactivate any active hit when search input is focused by a mouse click
input.addEventListener('focus', () => {
activeIndex = 0
deactivateHits()
})
// deactivate any active hit when typing in search box
input.addEventListener('keydown', event => {
2018-06-01 22:22:39 +03:00
if (!targetEventCodes.includes(event.code)) {
activeIndex = 0
deactivateHits()
}
})
document.addEventListener('keydown', event => {
2018-06-01 22:22:39 +03:00
// bail early if key code is not one that we're explicity expecting
if (!event || !event.code || !targetEventCodes.includes(keycode(event))) return
const hits = Array.from(document.querySelectorAll(hitsSelector))
switch (keycode(event)) {
2018-06-01 22:22:39 +03:00
case 'esc':
input.focus()
input.select()
input.value = ''
return
case '/':
// when the input is focused, `/` should have no special behavior
if (event.target !== input) {
input.focus()
input.select()
2018-06-20 23:48:12 +03:00
event.preventDefault() // prevent slash from being typed into input
2018-06-01 22:22:39 +03:00
}
break
case 'up':
// search input is the zero index (don't go beyond it)
if (activeIndex > 0) {
activeIndex--
event.preventDefault() // prevent window scrolling
}
2018-06-01 22:22:39 +03:00
updateActiveHit()
break
case 'down':
// last hit is the last index (don't go beyond it)
if (activeIndex < hits.length) {
activeIndex++
event.preventDefault() // prevent window scrolling
}
2018-06-01 22:22:39 +03:00
updateActiveHit()
break
case 'enter':
// look for a link in the given hit, then visit it
if (activeIndex > 0) {
const hit = hits[activeIndex - 1]
if (!hit) return
const link = hit.querySelector('a')
if (!link) return
const href = link.getAttribute('href')
if (!href) return
window.location = href
}
break
}
2018-06-01 22:22:39 +03:00
})
function deactivateHits () {
Array.from(document.querySelectorAll(hitsSelector)).forEach(hit => {
hit.classList.remove('active')
})
}
function updateActiveHit () {
2018-06-01 22:22:39 +03:00
deactivateHits()
if (activeIndex === 0) {
input.focus()
input.select()
} else {
const hits = Array.from(document.querySelectorAll(hitsSelector))
hits[activeIndex - 1].classList.add('active')
input.blur()
}
}
}