HjklNavigation

Shortcuts for Google Search result. j/k to move focus, l/h to open in new/background tab.

当前为 2021-05-02 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name HjklNavigation
  3. // @namespace com.gmail.fujifruity.greasemonkey
  4. // @version 1.5.2
  5. // @description Shortcuts for Google Search result. j/k to move focus, l/h to open in new/background tab.
  6. // @author fujifruity
  7. // @include https://www.google.com/search*
  8. // @grant GM.openInTab
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. {
  13. const items = [...document.querySelectorAll('#rso .g')].filter(e => e.className == 'g')
  14.  
  15. const open = isBackground => {
  16. const url = findCurrentItem().querySelector('a').href
  17. GM.openInTab(url, isBackground)
  18. }
  19. const tag = "hjklNavigationCurrentItem"
  20. const findCurrentItem = () => items.find(e => e.hasAttribute(tag))
  21.  
  22. const moveCursor = step => {
  23. const currentItem = findCurrentItem()
  24. const r = currentItem.getBoundingClientRect();
  25. const isVisible = 0 < r.top && r.top < window.innerHeight || 0 < r.bottom && r.bottom < window.innerHeight
  26. if (!isVisible) {
  27. const dist = e => {
  28. const r = e.getBoundingClientRect()
  29. return Math.abs(window.innerHeight - (r.top + r.bottom))
  30. }
  31. const nearest = items.reduce((acc,e) => dist(acc) < dist(e) ? acc : e )
  32. select(nearest)
  33. return
  34. }
  35. const nextIdx = (items.indexOf(currentItem) + step + items.length) % items.length
  36. select(items[nextIdx])
  37. }
  38. const highlight = e => {
  39. e.style.backgroundColor = 'lightyellow'
  40. e.setAttribute(tag, '')
  41. }
  42. const select = item => {
  43. // deselect current item
  44. const currentItem = findCurrentItem()
  45. currentItem?.setAttribute('style', "background-color: null;")
  46. currentItem?.removeAttribute(tag)
  47. highlight(item)
  48. item.scrollIntoView({ behavior: "smooth", block: "center" })
  49. }
  50. highlight(items[0])
  51.  
  52. window.addEventListener('keydown', event => {
  53. if (event.target.tagName == "INPUT" || event.ctrlKey || event.altKey) return
  54. switch (event.key) {
  55. case 'j': {
  56. moveCursor(+1)
  57. break
  58. }
  59. case 'k': {
  60. moveCursor(-1)
  61. break
  62. }
  63. case 'l': {
  64. open(false)
  65. break
  66. }
  67. case 'h': {
  68. open(true)
  69. break
  70. }
  71. }
  72. })
  73.  
  74. }