Scroll to nearest paragraph using ← and → keys with 50px offset, starting from current scroll position if already scrolled down the page (no modifier keys required)
当前为
// ==UserScript==
// @name Scroll to Nearest Paragraph (← → keys)
// @namespace http://tampermonkey.net/
// @version 1.5
// @description Scroll to nearest paragraph using ← and → keys with 50px offset, starting from current scroll position if already scrolled down the page (no modifier keys required)
// @author Işık Barış Fidaner
// @match *://*/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
let paragraphs = [];
let current = 0;
let initialized = false;
const offset = 50; // pixels above paragraph
function init() {
paragraphs = Array.from(document.querySelectorAll('p'))
.filter(p => p.offsetHeight > 0 && p.offsetParent !== null);
setInitialIndex();
initialized = true;
}
function setInitialIndex() {
const viewportTop = window.scrollY;
for (let i = 0; i < paragraphs.length; i++) {
const rect = paragraphs[i].getBoundingClientRect();
const paragraphTop = window.scrollY + rect.top;
if (paragraphTop - offset >= viewportTop) {
current = i;
return;
}
}
current = paragraphs.length; // in case scrolled past all
}
function scrollToParagraph(index) {
if (index >= 0 && index < paragraphs.length) {
const rect = paragraphs[index].getBoundingClientRect();
const scrollY = window.scrollY + rect.top - offset;
window.scrollTo({
top: Math.max(scrollY, 0),
behavior: 'smooth'
});
current = index;
}
}
document.addEventListener('keydown', (e) => {
// Ignore keypresses in input fields or editable areas
const target = e.target;
if (
target.tagName === 'INPUT' ||
target.tagName === 'TEXTAREA' ||
target.isContentEditable
) {
return;
}
if (!initialized) init();
if (e.key === 'ArrowRight') {
e.preventDefault();
if (current < paragraphs.length) {
scrollToParagraph(current);
current++;
}
} else if (e.key === 'ArrowLeft') {
e.preventDefault();
current = Math.max(current - 2, 0); // adjust for previous move
scrollToParagraph(current);
current++;
}
});
})();