NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Open Links in Background Tab
// @description Opens links in a new background tab next to the current tab
// @author Rami S Ejailat
// @copyright RamiEjailat (https://openuserjs.org/users/RamiEjailat)
// @updateURL https://openuserjs.org/meta/RamiEjailat/Open_Links_in_Background_Tab.meta.js
// @downloadURL https://openuserjs.org/install/RamiEjailat/Open_Links_in_Background_Tab.user.js
// @license MIT
// @version 20260429
// @namespace http://tampermonkey.net/
// @match *://*/*
// @grant GM_openInTab
// @icon https://cdn-icons-png.flaticon.com/64/4906/4906292.png
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
// ─── Helpers ─────────────────────────────────────────────────────────────
function shouldIntercept(target, e) {
if (!target || !target.href) return false;
const href = target.href;
if (href === '#') return false;
if (href.startsWith('javascript:')) return false;
if (href.startsWith(window.location.href.split('#')[0] + '#')) return false;
if (e && (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey)) return false;
return true;
}
function openInBackground(href) {
const originalUrl = window.location.href;
GM_openInTab(href, { active: false, insert: true, setParent: true });
// Mark link as visited without navigating
window.history.replaceState({}, '', href);
window.history.replaceState({}, '', originalUrl);
// Safety net: if a SPA router reacted to the push, restore after 200ms.
setTimeout(() => {
if (window.location.href !== originalUrl) {
window.history.replaceState({}, '', originalUrl);
}
}, 200);
}
function intercept(e, href) {
e.preventDefault();
e.stopImmediatePropagation();
openInBackground(href);
}
// ─── Touch ───────────────────────────────────────────────────────────────
const TAP_THRESHOLD_PX = 10;
let touchAnchor = null;
let touchOrigin = { x: 0, y: 0 };
document.addEventListener('touchstart', function (e) {
touchAnchor = null;
const target = e.target.closest('a');
if (!shouldIntercept(target, e)) return;
const touch = e.touches[0];
touchOrigin = { x: touch.clientX, y: touch.clientY };
touchAnchor = target;
// Passive and no preventDefault so that scroll stays free
}, { capture: true, passive: true });
document.addEventListener('touchend', function (e) {
if (!touchAnchor) return;
const touch = e.changedTouches[0];
const moved = Math.abs(touch.clientX - touchOrigin.x) > TAP_THRESHOLD_PX
|| Math.abs(touch.clientY - touchOrigin.y) > TAP_THRESHOLD_PX;
const anchor = touchAnchor;
touchAnchor = null;
if (!moved) { // Confirmed tap
intercept(e, anchor.href);
}
}, { capture: true, passive: false }); // passive: false required for preventDefault
// ─── Context menu: right-click / long-press ───────────────────────────────
document.addEventListener('contextmenu', function (e) {
const target = e.target.closest('a');
if (!shouldIntercept(target, e)) return;
intercept(e, target.href);
}, true);
})();