// ==UserScript== // @name HjklNavigation // @namespace com.gmail.fujifruity.greasemonkey // @version 1.5.1 // @description Shortcuts for Google Search result. j/k to move focus, l/h to open in new/background tab. // @author fujifruity // @include https://www.google.com/search* // @grant GM.openInTab // @license MIT // @downloadURL none // ==/UserScript== { const items = [...document.querySelectorAll('#rso .g')].filter(e => e.className == 'g') const open = isBackground => { const url = findCurrentItem().querySelector('a').href GM.openInTab(url, isBackground) } const getVisibleItems = () => items.filter(e => { const r = e.getBoundingClientRect(); return 0 < r.top && r.top < window.innerHeight || 0 < r.bottom && r.bottom < window.innerHeight }) const tag = "hjklNavigationCurrentItem" const findCurrentItem = () => items.find(e => e.hasAttribute(tag)) const moveCursor = step => { const currentItem = findCurrentItem() const visibleItems = getVisibleItems() if (!visibleItems.includes(currentItem)) { const centerVisible = visibleItems[parseInt(visibleItems.length / 2)] select(centerVisible) return } const nextIdx = (items.indexOf(currentItem) + step + items.length) % items.length select(items[nextIdx]) } const select = item => { // deselect current item const currentItem = findCurrentItem() currentItem?.setAttribute('style', "background-color: null;") currentItem?.removeAttribute(tag) item.style.backgroundColor = 'lightyellow' item.setAttribute(tag, '') item.scrollIntoView({ behavior: "smooth", block: "center" }) } items[0].style.backgroundColor = 'lightyellow' items[0].setAttribute(tag, '') window.addEventListener('keydown', event => { if (event.target.tagName == "INPUT" || event.ctrlKey || event.altKey) return switch (event.key) { case 'j': { moveCursor(+1) break } case 'k': { moveCursor(-1) break } case 'l': { open(false) break } case 'h': { open(true) break } } }) }