Skip to content

Instantly share code, notes, and snippets.

@udaken
Last active October 25, 2024 14:36
Show Gist options
  • Save udaken/72334ce6086a47eb746fa3a6a8cc777a to your computer and use it in GitHub Desktop.
Save udaken/72334ce6086a47eb746fa3a6a8cc777a to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Google Search Keyboard Navigation
// @namespace http://tampermonkey.net/
// @version 0.7
// @description Navigate Google search results using 'j' (down), 'k' (up), and Enter (open) keys
// @author Your name
// @match https://www.google.com/search?*
// @grant GM_addStyle
// @downloadURL https://gist.github.com/udaken/72334ce6086a47eb746fa3a6a8cc777a/raw/google_search_keyboard_navigation.user.js
// @updateURL https://gist.github.com/udaken/72334ce6086a47eb746fa3a6a8cc777a/raw/google_search_keyboard_navigation.user.js
// ==/UserScript==
(function() {
'use strict';
// CSSスタイルを追加
GM_addStyle(`
div.g[tabindex="-1"]:focus {
outline: none;
background-color: #E8F0FE !important;
border-radius: 4px;
}
div.g[tabindex="-1"]:focus * {
background-color: transparent !important;
}
`);
let currentIndex = -1;
const searchResults = [];
function initializeSearchResults() {
// Google検索結果の要素を取得し、tabindex属性を追加
const results = document.querySelectorAll('div.g');
searchResults.length = 0;
results.forEach(result => {
result.setAttribute('tabindex', '-1');
searchResults.push(result);
});
}
function focusResult(index) {
// 現在の結果からフォーカスを外す
if (currentIndex >= 0 && currentIndex < searchResults.length) {
searchResults[currentIndex].blur();
}
// 新しい結果にフォーカス
if (index >= 0 && index < searchResults.length) {
const resultElement = searchResults[index];
resultElement.focus();
resultElement.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
}
function moveToNextResult() {
if (searchResults.length === 0) {
initializeSearchResults();
}
currentIndex++;
if (currentIndex >= searchResults.length) {
currentIndex = 0;
}
focusResult(currentIndex);
}
function moveToPreviousResult() {
if (searchResults.length === 0) {
initializeSearchResults();
}
currentIndex--;
if (currentIndex < 0) {
currentIndex = searchResults.length - 1;
}
focusResult(currentIndex);
}
function openCurrentResult(event) {
if (currentIndex >= 0 && currentIndex < searchResults.length) {
const link = searchResults[currentIndex].querySelector('a');
if (link) {
if (event.ctrlKey) {
window.open(link.href, '_blank');
} else {
window.location.href = link.href;
}
}
}
}
// 最初の結果を選択する
function selectFirstResult() {
if (currentIndex === -1 && searchResults.length > 0) {
currentIndex = 0;
focusResult(currentIndex);
}
}
// キーボードイベントリスナーを追加
document.addEventListener('keydown', function(e) {
// 入力フィールドやテキストエリアでの入力時は動作しないようにする
if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') {
return;
}
// Alt と組み合わせた場合は動作しない
if (e.altKey || e.shiftKey || e.ctrlKey) {
return;
}
switch (e.key) {
case 'j':
moveToNextResult();
e.preventDefault();
break;
case 'k':
moveToPreviousResult();
e.preventDefault();
break;
case 'v':
if (currentIndex !== -1) {
openCurrentResult(e);
e.preventDefault();
}
break;
}
});
// ページロード時の初期化
function initialize() {
initializeSearchResults();
selectFirstResult();
}
// ページロード時に初期化
initialize();
// 動的に結果が追加される可能性があるため、定期的に結果を更新
const observer = new MutationObserver(() => {
initializeSearchResults();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment