Skip to content

Instantly share code, notes, and snippets.

@szupie
Last active October 18, 2022 21:30
Show Gist options
  • Save szupie/d50a7b9162b42bd352a96b7bebf50b5d to your computer and use it in GitHub Desktop.
Save szupie/d50a7b9162b42bd352a96b7bebf50b5d to your computer and use it in GitHub Desktop.
UserScript to enable use of tab key to navigate through Google search results. Also available as extension: https://github.com/szupie/tab-focus-google
// ==UserScript==
// @name Tab Focus through Google Search Results
// @description Use the tab key to navigate through Google and DuckDuckGo search results
// @version 1.1.3
// @match *://*/search*
// @include *://*.google.*/search*
// @match *://*.duckduckgo.com/*
// @grant none
// @author szupie szupie@gmail.com
// @namespace szupie
// ==/UserScript==
(function () {
'use strict';
let selectors;
function init() {
const results = document.querySelectorAll(selectors['resultTitle']);
for (let i=0; i<results.length; i++) {
const linkNode = results[i].closest('a');
linkNode.setAttribute('tabindex', 1);
}
// capture focus changes on results list to scroll entire result into view
document.querySelector(selectors['resultsDiv']).addEventListener("focus", e => {
// only perform scroll if newly focused element is result link
if (e.target.getAttribute('tabindex') === '1') {
const resultNode = e.target.closest(selectors['resultNode']);
const bounds = resultNode.getBoundingClientRect();
// scroll item to top if it extends past viewport top,
// or to bottom if it extends past viewport bottom
if (bounds.top < 0) {
resultNode.scrollIntoView();
} else if (bounds.bottom > window.innerHeight) {
resultNode.scrollIntoView(false);
}
}
}, true);
const styleNode = document.createElement('style');
styleNode.type = 'text/css';
styleNode.innerHTML = css;
styleNode.id = 'tab-focus-results';
document.getElementsByTagName('head')[0].appendChild(styleNode);
}
// CSS selectors
const googleSelectors = {
'resultTitle': '.LC20lb',
'resultsDiv': '#res',
'resultNode': '.g'
};
const ddgSelectors = {
'resultTitle': '#links a.result__a:not(.result__sitelink-title)',
'resultsDiv': '#links',
'resultNode': '.result'
};
if (window.location.hostname != 'duckduckgo.com') {
selectors = googleSelectors;
} else {
selectors = ddgSelectors;
}
// Style to indicate focus
const css =
`a[tabindex="1"]:focus::after {
content: '►';
position: absolute;
right: 100%;
margin-top: 1em;
margin-right: 2px;
color: #4273DB;
font: 11px arial, sans-serif;
}
a[tabindex="1"]:focus h3 {
outline: 2px solid;
}
a[tabindex="1"]:focus {
outline: none;
}
/* for duckduckgo */
a.result__a[tabindex="1"]:focus::after {
margin-top: 0.25em;
margin-right: 0;
}`;
if (document.querySelector(selectors['resultTitle'])) {
init();
} else {
window.addEventListener('load', init);
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment