您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Change color of clicked links in body with color presets and custom hex input
// ==UserScript== // @name Change clicked link's color // @license MIT // @namespace http://tampermonkey.net/ // @author [email protected] // @homepageURL https://greasyfork.org/vi/scripts/501244-change-clicked-link-s-color // @version 1.2.6 // @description Change color of clicked links in body with color presets and custom hex input // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_addStyle // @icon https://cdn-icons-png.flaticon.com/512/4906/4906292.png // ==/UserScript== (function() { 'use strict'; // Config let p_color_clicked = GM_getValue('colorClicked', '#800080'); let p_apply_all = GM_getValue('applyAll', true); let p_apply_domains = GM_getValue('applyDomains', ''); // Variables const style_id = "clicked-link-color-style"; const css_a_clicked = ` a:visited:not(nav a):not(.nav a):not(.navbar a):not(.menu a):not(.navigation a), a:visited:not(nav a):not(.nav a):not(.navbar a):not(.menu a):not(.navigation a) *, a.custom-visited, a.custom-visited * { color: %COLOR% !important; }`; const colorPresets = { 'Purple': '#800080', 'Red': '#FF0000', 'Blue': '#0000FF', 'Green': '#008000', 'Orange': '#FFA500', 'Pink': '#FFC0CB', 'Brown': '#A52A2A', 'Gray': '#808080', 'Cyan': '#00FFFF', 'Magenta': '#FF00FF', 'Lime': '#00FF00' }; // Functions function isDomainApplied(domains, site) { if (p_apply_all) return true; if (domains.trim() === '') return false; let domainList = domains.split(","); return domainList.some(domain => site.includes(domain.trim())); } function addStyle(css) { let style = document.getElementById(style_id); if (style === null) { let head = document.getElementsByTagName("head")[0]; style = document.createElement("style"); style.setAttribute("id", style_id); style.setAttribute("type", "text/css"); head.appendChild(style); } style.textContent = css; } function assignColor(css, color) { return css.replace(/%COLOR%/ig, color); } function main() { let url = document.documentURI; let css = assignColor(css_a_clicked, p_color_clicked); if (isDomainApplied(p_apply_domains, url)) { addStyle(css); addClickListener(); } } function addClickListener() { document.body.addEventListener('click', (e) => { if (e.target.tagName === 'A') { e.target.classList.add('custom-visited'); } }); } function observeDOMChanges() { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { main(); } }); }); observer.observe(document.body, { childList: true, subtree: true }); } function createDialog(id, content) { const dialog = document.createElement('dialog'); dialog.className = 'custom-dialog'; dialog.id = id; dialog.innerHTML = ` <form method="dialog"> ${content} </form> `; document.body.appendChild(dialog); dialog.style.position = 'fixed'; dialog.style.left = '50%'; dialog.style.top = '50%'; dialog.style.transform = 'translate(-50%, -50%)'; dialog.style.maxWidth = '400px'; dialog.style.width = '90%'; dialog.style.border = '2px solid #007bff'; dialog.style.borderRadius = '8px'; dialog.style.padding = '16px'; dialog.style.backgroundColor = '#fff'; dialog.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)'; dialog.style.overflow = 'hidden'; dialog.showModal(); return dialog; } function showColorSelector() { const content = ` <h3>Change Clicked Link's Color</h3> <div class="section"> <label for="colorPreset">Choose a preset color:</label> <select id="colorPreset" style="width: 100%;"> <option value="">-- Choose a preset color --</option> ${Object.entries(colorPresets).map(([name, value]) => `<option value="${value}" style="background-color: ${value}; color: white;">${name} (${value})</option>` ).join('')} </select> </div> <div class="section"> <label for="customColor">Or enter a custom hex color:</label> <div style="display: flex; align-items: center; gap: 10px;"> <input type="text" id="customColor" placeholder="#RRGGBB" pattern="^#[0-9A-Fa-f]{6}$" style="flex: 1; height: 2em; border: 2px solid #007bff; border-radius: 4px;"> <input type="color" id="colorPicker" value="${p_color_clicked}" style="height: 2em; border: 2px solid #007bff; border-radius: 4px;"> </div> </div> <div class="button-container" style="margin-top: 20px;"> <button type="submit" id="applyBtn" style="background-color: #007bff; color: #fff; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; margin-right: 10px;">Apply</button> <button type="button" id="cancelBtn" style="background-color: #ccc; color: #000; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer;">Cancel</button> </div> `; const dialog = createDialog('colorDialog', content); const colorPreset = dialog.querySelector('#colorPreset'); const customColor = dialog.querySelector('#customColor'); const colorPicker = dialog.querySelector('#colorPicker'); const applyBtn = dialog.querySelector('#applyBtn'); const cancelBtn = dialog.querySelector('#cancelBtn'); colorPreset.value = p_color_clicked; customColor.value = p_color_clicked; colorPicker.value = p_color_clicked; colorPreset.addEventListener('change', function() { if (this.value) { customColor.value = this.value; colorPicker.value = this.value; } }); colorPicker.addEventListener('input', function() { customColor.value = this.value; }); cancelBtn.addEventListener('click', () => dialog.close()); dialog.addEventListener('close', () => { if (dialog.returnValue !== 'cancel') { const newColor = customColor.value; if (/^#[0-9A-Fa-f]{6}$/i.test(newColor)) { p_color_clicked = newColor; GM_setValue('colorClicked', p_color_clicked); main(); } else { alert("Invalid color code. Please use hex format (e.g., #800080)."); } } }); } function showDomainSettings() { const content = ` <h3>Manage Enabled Domains</h3> <div class="section"> <label for="enabledSites">Enable on these sites (one per line):</label> <textarea id="enabledSites" rows="10" style="width: 100%; border: 2px solid #007bff; border-radius: 4px; padding: 8px; box-sizing: border-box;">${p_apply_domains.replace(/,/g, '\n')}</textarea> </div> <div class="section" style="margin-top: 10px;"> <label> <input type="checkbox" id="enableAllSites" ${p_apply_all ? 'checked' : ''}> Apply to all websites </label> </div> <div class="button-container" style="display: flex; justify-content: space-between; margin-top: 20px;"> <button type="button" id="addThisSite" style="background-color: #28a745; color: #fff; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer;">Add this site</button> <div class="button-group" style="display: flex; gap: 10px;"> <button type="submit" style="background-color: #007bff; color: #fff; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer;">Save</button> <button type="button" id="cancelBtn" style="background-color: #ccc; color: #000; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer;">Cancel</button> </div> </div> `; const dialog = createDialog('domainDialog', content); const enabledSitesTextarea = dialog.querySelector('#enabledSites'); const addThisSiteBtn = dialog.querySelector('#addThisSite'); const saveBtn = dialog.querySelector('button[type="submit"]'); const cancelBtn = dialog.querySelector('#cancelBtn'); const enableAllSitesCheckbox = dialog.querySelector('#enableAllSites'); addThisSiteBtn.addEventListener('click', () => { const currentDomain = window.location.hostname.replace(/^www\./, ''); const domainList = enabledSitesTextarea.value.split('\n').map(site => site.trim()).filter(Boolean); if (!domainList.includes(currentDomain)) { domainList.push(currentDomain); enabledSitesTextarea.value = domainList.join('\n'); } }); cancelBtn.addEventListener('click', () => dialog.close('cancel')); dialog.addEventListener('close', () => { if (dialog.returnValue !== 'cancel') { const newEnabledSites = enabledSitesTextarea.value.split('\n').map(site => site.trim()).filter(Boolean); const newEnableAllSites = enableAllSitesCheckbox.checked; if (JSON.stringify(newEnabledSites) !== JSON.stringify(p_apply_domains.split(',')) || newEnableAllSites !== p_apply_all) { p_apply_domains = newEnabledSites.join(','); p_apply_all = newEnableAllSites; GM_setValue('applyDomains', p_apply_domains); GM_setValue('applyAll', p_apply_all); main(); } } }); } // Menu commands GM_registerMenuCommand("🎨 Change clicked link's color", showColorSelector, "C"); GM_registerMenuCommand("🌐 Domain settings", showDomainSettings, "D"); // Run main function immediately main(); // Observe DOM changes observeDOMChanges(); })();