您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Replace all Twitter emoji images (Twemoji) on x.com with native system emojis.
- // ==UserScript==
- // @name Replace Twitter/X Emojis with System Emojis
- // @namespace http://tampermonkey.net/
- // @version 0.3
- // @description Replace all Twitter emoji images (Twemoji) on x.com with native system emojis.
- // @author Your Name (Updated)
- // @match https://x.com/*
- // @grant none
- // @license MIT
- // ==/UserScript==
- (function() {
- 'use strict';
- // Selector for Twitter's emoji images (covers SVG and potentially PNG)
- const EMOJI_SELECTOR = 'img[src*="/emoji/v2/"][alt]'; // Added alt requirement to selector
- /**
- * Replaces a given Twitter emoji IMG node with a SPAN containing the native system emoji.
- * @param {HTMLImageElement} imgNode The emoji image element to replace.
- */
- function replaceEmojiNode(imgNode) {
- // Check if already processed or lacks necessary attributes
- if (imgNode.dataset.emojiReplaced || !imgNode.parentNode || !imgNode.alt) {
- return;
- }
- const alt = imgNode.alt; // The actual emoji character(s)
- const span = document.createElement('span');
- span.textContent = alt; // Use the exact alt text (handles single/multiple emojis)
- span.setAttribute('role', 'img'); // Accessibility: Treat span like an image
- span.setAttribute('aria-label', alt); // Accessibility: Provide label
- // --- Styling ---
- // 1. Basic inline behavior and alignment (adjust if needed)
- span.style.display = 'inline-block'; // Or 'inline' might work depending on context
- span.style.verticalAlign = 'text-bottom'; // Common alignment, adjust based on visual tests
- // 2. Try to match the size of the original emoji image
- try {
- const computedStyle = window.getComputedStyle(imgNode);
- const height = computedStyle.height;
- // const width = computedStyle.width; // Width can be tricky with multiple chars, primarily use height
- // Use height for font size and line height for better vertical centering and scaling
- if (height && height !== 'auto' && parseFloat(height) > 0) {
- span.style.fontSize = height;
- span.style.lineHeight = height; // Match line height to font size for vertical centering
- span.style.height = height; // Explicit height
- // Setting width can sometimes be problematic for multi-character emojis or variable-width system emojis.
- // Let the browser determine width based on content and font size unless specific issues arise.
- // span.style.width = 'auto'; // Allow natural width based on font/content
- } else {
- // Fallback size if computed style is unavailable or invalid
- span.style.fontSize = '1em'; // Inherit from parent or use a reasonable default
- span.style.lineHeight = '1'; // Normal line height
- }
- // Copy other potentially relevant styles (optional, can add complexity)
- // span.style.margin = computedStyle.margin;
- } catch (e) {
- console.warn("Replace Emojis Script: Couldn't get computed style for emoji, using default size.", e);
- // Apply fallback size on error
- span.style.fontSize = '1em';
- span.style.lineHeight = '1';
- }
- // Mark the original node as processed BEFORE replacing it
- imgNode.dataset.emojiReplaced = 'true';
- imgNode.style.display = 'none'; // Hide original immediately to prevent flash
- // Replace the image node with the new span node
- imgNode.parentNode.replaceChild(span, imgNode);
- // console.log(`Replaced emoji: ${alt}`); // For debugging
- }
- /**
- * Scans a given node and its descendants for emoji images and replaces them.
- * @param {Node} targetNode The node to scan.
- */
- function processNode(targetNode) {
- if (!targetNode || !targetNode.querySelectorAll) return;
- // Find all emoji images within the target node that haven't been replaced
- const emojiImages = targetNode.querySelectorAll(EMOJI_SELECTOR + ':not([data-emoji-replaced="true"])');
- emojiImages.forEach(replaceEmojiNode);
- }
- // --- Mutation Observer ---
- // Observe DOM changes to catch dynamically loaded content
- const observer = new MutationObserver(mutations => {
- mutations.forEach(mutation => {
- if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
- mutation.addedNodes.forEach(node => {
- // Check if the added node itself is an emoji
- if (node.nodeType === Node.ELEMENT_NODE && node.matches(EMOJI_SELECTOR)) {
- replaceEmojiNode(node);
- }
- // Check if the added node contains emojis
- else if (node.nodeType === Node.ELEMENT_NODE && node.querySelector) {
- processNode(node);
- }
- });
- }
- // Optional: Handle attribute changes if emoji src/alt might change later
- // else if (mutation.type === 'attributes' && mutation.attributeName === 'src' && mutation.target.matches(EMOJI_SELECTOR)) {
- // // Re-evaluate if needed, but usually replacement is permanent
- // // Be careful not to cause infinite loops if you modify attributes observer listens to
- // }
- });
- });
- // Start observing the document body for additions and subtree modifications
- observer.observe(document.body, {
- childList: true,
- subtree: true
- // attributes: true, // Uncomment cautiously if needed
- // attributeFilter: ['src', 'alt'] // Specify attributes if uncommenting 'attributes'
- });
- // --- Initial Scan ---
- // Process existing emojis present on the page when the script initially runs
- console.log("Replace Emojis Script: Running initial scan...");
- processNode(document.body);
- console.log("Replace Emojis Script: Initial scan complete. Observer active.");
- })();