Justifies everything unless they are centered or aligned to the right.
// ==UserScript==
// @name Smart Text Justifier
// @namespace http://tampermonkey.net/
// @version 1.5
// @description Justifies everything unless they are centered or aligned to the right.
// @author Ȼaptain Jøhn “Søap” MacTavish
// @license CC-BY-NC-SA-4.0
// @match *://*/*
// @exclude https://www.grok.com/*
// @exclude https://www.chatgpt.com/*
// @grant none
// ==/UserScript==
(
function()
{
'use strict';
const ProcessedElements = new WeakSet();
const IgnoredTags = new Set
([
'SCRIPT', 'STYLE', 'META', 'LINK', 'HEAD', 'TITLE', 'NOSCRIPT',
'BR', 'HR', 'IMG', 'VIDEO', 'AUDIO', 'CANVAS', 'SVG', 'PATH',
'IFRAME', 'OBJECT', 'EMBED', 'CODE', 'PRE', 'A', 'SPAN', 'BUTTON',
'LABEL', 'INPUT', 'SELECT', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6',
'STRONG', 'B', 'I', 'EM', 'SMALL', 'SUB', 'SUP', 'KBD', 'ABBR',
'CITE', 'DD', 'DT', 'NAV', 'HEADER', 'FOOTER'
]);
const CheckAndQueueElement = (NodeToCheck, ElementsQueue) =>
{
if (!ProcessedElements.has(NodeToCheck) && !IgnoredTags.has(NodeToCheck.tagName))
{
ProcessedElements.add(NodeToCheck);
const ComputedAlignment = window.getComputedStyle(NodeToCheck).textAlign;
if (ComputedAlignment !== 'center' && ComputedAlignment !== 'right')
{
ElementsQueue.push(NodeToCheck);
}
}
};
const ProcessElements = (Elements) =>
{
const ElementsToUpdate = [];
// DOM READ PHASE: Prevents Layout Thrashing by batching all reads first.
for (const Element of Elements)
{
if (Element.nodeType === 1)
{
CheckAndQueueElement(Element, ElementsToUpdate);
const Children = Element.querySelectorAll('*');
for (const Child of Children)
{
CheckAndQueueElement(Child, ElementsToUpdate);
}
}
}
// DOM WRITE PHASE: Applies styles without triggering style recalculations mid-loop.
for (const Element of ElementsToUpdate)
{
Element.style.setProperty('text-align', 'justify', 'important');
Element.style.setProperty('text-justify', 'inter-word', 'important');
}
};
const InitializeObserver = () =>
{
const InitialElements = [ document.body ];
ProcessElements(InitialElements);
let PendingNodes = [];
let IsProcessing = false;
const ContentObserver = new MutationObserver
(
(Mutations) =>
{
for (const Mutation of Mutations)
{
for (const Node of Mutation.addedNodes)
{
if (Node.nodeType === 1)
{
PendingNodes.push(Node);
}
}
}
if (!IsProcessing && PendingNodes.length > 0)
{
IsProcessing = true;
requestAnimationFrame
(
() =>
{
ProcessElements(PendingNodes);
PendingNodes = [];
IsProcessing = false;
}
);
}
}
);
ContentObserver.observe
(
document.body,
{
childList: true,
subtree: true
}
);
};
if (document.readyState === 'complete' || document.readyState === 'interactive')
{
InitializeObserver();
}
else
{
window.addEventListener('DOMContentLoaded', InitializeObserver);
}
}
)
();