Skip to content

Instantly share code, notes, and snippets.

@gibson042
Last active November 9, 2023 18:01
Show Gist options
  • Save gibson042/73dfb17bfa7d35a9fb2e4c396365c321 to your computer and use it in GitHub Desktop.
Save gibson042/73dfb17bfa7d35a9fb2e4c396365c321 to your computer and use it in GitHub Desktop.
Anchors Up user script
// ==UserScript==
// @name Anchors Up
// @namespace https://github.com/gibson042
// @description Navigates to in-page anchors upon {Command,Ctrl}+click or (with Shift) scrolls to arbitrary elements.
// @source https://gist.github.com/gibson042/73dfb17bfa7d35a9fb2e4c396365c321
// @updateURL https://gist.github.com/gibson042/73dfb17bfa7d35a9fb2e4c396365c321/raw/anchors-up.user.js
// @downloadURL https://gist.github.com/gibson042/73dfb17bfa7d35a9fb2e4c396365c321/raw/anchors-up.user.js
// @version 0.5.0
// @date 2023-09-08
// @author Richard Gibson <@gmail.com>
// @include *
// ==/UserScript==
//
// **COPYRIGHT NOTICE**
//
// To the extent possible under law, the author(s) have dedicated all copyright
// and related and neighboring rights to this software to the public domain
// worldwide. This software is distributed without any warranty.
// For the CC0 Public Domain Dedication, see
// <https://creativecommons.org/publicdomain/zero/1.0/>.
//
// **END COPYRIGHT NOTICE**
//
//
// Changelog:
// 0.5.0 (2023-09-08)
// * New: CC0 Public Domain Dedication.
// 0.4.0 (2022-11-30)
// * New: Scroll to arbitrary locations with {Command,Ctrl}+Shift+click.
// 0.3.3 (2022-03-21)
// * Improved: Better OS and user script manager compatibility.
// 0.3.1 (2022-02-02)
// * Fixed: Require that Ctrl be the *only* active modifier key.
// 0.3.0 (2019-06-03)
// * Improved: Descend into single-child wrapping elements.
// 0.2.0 (2018-06-26)
// * Improved: Text node children stand in for click targets.
// 0.1.0 (2018-03-21)
// original release
(function() {
"use strict";
// When traversing the DOM seeking a parent anchor, allow detours to a bounded number of previous siblings.
const PREV_LIMIT = 1;
document.addEventListener("click", function( evt ) {
// If Command/Control is not pressed or any other modifier key other than Shift is, abort.
if ( !((evt.ctrlKey ^ evt.metaKey) && !evt.altKey) ) {
return;
}
let anchor,
el = evt.target,
prev = 0;
// If Shift is pressed, scroll directly to the target.
if (evt.shiftKey) {
el.scrollIntoView();
const selection = window.getSelection();
selection.collapseToEnd(selection.focusNode, selection.focusOffset);
return;
}
// If the target has child text nodes, let the first one stand in for it.
if ( el.childNodes.length > el.childElementCount ) {
el = Array.prototype.find.call(
el.childNodes,
function(node){ return node.nodeType === document.TEXT_NODE }
);
}
while ( el ) {
// Upon finding a link ancestor, quit to allow native navigation.
if ( prev === 0 && el.nodeName.toLowerCase() === "a" && el.href ) {
return;
}
// Check for an anchor, allowing descent into single-child parents
// such as wrapping <p>s from Markdown rendering.
anchor = el.id || el.name;
if ( !anchor && el.childElementCount === 1 ) {
anchor = el.firstElementChild.id || el.firstElementChild.name;
}
// Upon finding an anchor, navigate to it and quit.
if ( anchor ) {
location.assign("#" + anchor);
return;
}
// Otherwise, traverse back or up.
el = (++prev <= PREV_LIMIT && el.previousElementSibling) || (prev = 0, el.parentNode);
}
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment