Skip to content

Instantly share code, notes, and snippets.

@scarlac
Last active April 26, 2026 19:12
Show Gist options
  • Select an option

  • Save scarlac/a1d00eece75c7f199bbc906a6269ac52 to your computer and use it in GitHub Desktop.

Select an option

Save scarlac/a1d00eece75c7f199bbc906a6269ac52 to your computer and use it in GitHub Desktop.
UserScript for forcing links to open in the same tab
// ==UserScript==
// @name Force links to open in same window
// @namespace https://github.com/scarlac
// @version 1.1.0
// @description Rewrite target="_blank" (and similar) on links, areas, forms, and base tags so navigation stays in the current window/tab. Does not touch window.open or other JS APIs.
// @author scarlac
// @match *://*/*
// @run-at document-start
// @grant none
// @noframes
// ==/UserScript==
/**
* This script was written to address the user-hostile practice
* where many websites decide that you MUST open a new tab, as if you had no capacity to manage it yourself.
* The modern equivalent of pop-ups from the 90'ies.
* And since Chrome does not seem to have a native feature to disable this, you end up with a million mental threads.
* The Observer is necessary for sites like React. It does not fix all links as some sites use window.open and other dynamic tricks.
*
* Tested with TamperMonkey for Chrome.
**/
(function () {
'use strict';
const BAD_TARGETS = new Set(['_blank', '_new', 'blank', 'new']);
const SELECTOR = 'a[target], area[target], form[target], base[target]';
function fixTarget(el) {
if (!el || !el.getAttribute) return;
const t = el.getAttribute('target');
if (t && BAD_TARGETS.has(t.toLowerCase())) {
el.setAttribute('target', '_self');
}
}
function scan(root) {
if (!root || !root.querySelectorAll) return;
root.querySelectorAll(SELECTOR).forEach(fixTarget);
}
// Initial sweep as soon as the DOM is parseable.
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => scan(document), { once: true });
} else {
scan(document);
}
// Watch for dynamically added/changed elements (SPAs, infinite scroll, etc.).
const mo = new MutationObserver((mutations) => {
for (const m of mutations) {
if (m.type === 'attributes') {
fixTarget(m.target);
} else {
m.addedNodes.forEach((node) => {
if (node.nodeType !== 1) return; // Element nodes only
fixTarget(node);
scan(node);
});
}
}
});
const startObserver = () => {
mo.observe(document.documentElement || document, {
subtree: true,
childList: true,
attributes: true,
attributeFilter: ['target'],
});
};
if (document.documentElement) {
startObserver();
} else {
document.addEventListener('readystatechange', startObserver, { once: true });
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment