您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds buttons linking you to Robinhood from both the single stock page and the screener list.
// ==UserScript== // @name Finviz Redirect to Robinhood // @namespace http://tampermonkey.net/ // @version 1.5 // @description Adds buttons linking you to Robinhood from both the single stock page and the screener list. // @author Game Abuse Studios // @match https://elite.finviz.com/* // @license MIT // @grant GM_addStyle // @grant window.open // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // --- Inject Global Styles --- GM_addStyle(` @keyframes scanline { 0% { transform: translateY(-100%); } 100% { transform: translateY(100%); } } .tech-btn { border: 2px solid transparent; background: linear-gradient(#1a1f2b, #1a1f2b) padding-box, linear-gradient(to right, royalblue, blueviolet) border-box; color: #c9d1d9; font-family: 'monospace', 'Lucida Console', Monaco, Consolas; font-weight: bold; box-shadow: 0 2px 5px rgba(0,0,0,0.4); transition: all 0.2s ease-in-out; cursor: pointer; position: relative; overflow: hidden; touch-action: manipulation; } .tech-btn:hover, .tech-btn.active { background: linear-gradient(#2a2f3b, #2a2f3b) padding-box, linear-gradient(to right, dodgerblue, mediumorchid) border-box; color: #ffffff; transform: scale(1.03); } .tech-btn::after { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(to bottom, transparent 0%, rgba(138, 43, 226, 0.1) 50%, transparent 100%); opacity: 0; transform: translateY(-100%); transition: opacity 0.2s ease; } .tech-btn.scanline-active::after { opacity: 1; animation: scanline 0.6s linear; } `); // --- Unified Activation Function --- const handleActivation = (button, ticker) => { if (event) { event.preventDefault(); event.stopPropagation(); } button.classList.add('active', 'scanline-active'); window.open(`https://robinhood.com/stocks/${ticker}`, '_blank'); setTimeout(() => { button.classList.remove('active', 'scanline-active'); }, 600); }; // --- Script for Single Stock Page --- // (This part doesn't need event delegation as the button is not re-rendered) function setupSingleStockButton() { function waitForElement(selector, callback) { const element = document.querySelector(selector); if (element) { callback(element); } else { const observer = new MutationObserver((mutations, obs) => { const foundElement = document.querySelector(selector); if (foundElement) { obs.disconnect(); callback(foundElement); } }); observer.observe(document.body, { childList: true, subtree: true }); } } waitForElement('.js-recent-quote-ticker', (tickerElement) => { const ticker = tickerElement.textContent.trim().replace(/-/g, '.'); const button = document.createElement('button'); button.className = 'tech-btn'; button.textContent = `Open ${ticker} In Robinhood`; button.title = `Go to ${ticker} on Robinhood`; button.style.cssText = ` position: fixed; bottom: 20px; left: 20px; z-index: 1000; padding: 14px 28px; font-size: 20px; border-radius: 9999px; `; button.addEventListener('touchstart', (e) => handleActivation(button, ticker), { passive: false }); button.addEventListener('click', (e) => handleActivation(button, ticker)); document.body.appendChild(button); }); } // --- Script for Screener Page (Modified for Event Delegation) --- function setupScreenerButtons() { GM_addStyle(` .rh-floating-btn { height: 18px; font-size: 12px; border-radius: 9999px; padding: 0 5px; display: flex; align-items: center; justify-content: center; z-index: 9998; border-width: 1px; width: auto; touch-action: manipulation; } `); // The logic for adding buttons remains mostly the same, but the event listener is moved. const updateButtons = () => { const tickerLinks = document.querySelectorAll('a.tab-link[href*="quote.ashx?t="]'); tickerLinks.forEach(link => { const parentCell = link.closest('td'); if (!parentCell || parentCell.querySelector('.rh-floating-btn')) return; const originalTicker = link.textContent.trim(); const robinhoodTicker = originalTicker.replace('-', '.'); const button = document.createElement('button'); button.className = 'tech-btn rh-floating-btn'; button.textContent = originalTicker; button.dataset.ticker = robinhoodTicker; // Store ticker on the element parentCell.insertBefore(button, link); link.remove(); }); }; const screenerTable = document.getElementById('screener-views-table'); if (screenerTable) { // Attach ONE event listener to the parent table screenerTable.addEventListener('touchstart', (e) => { const targetButton = e.target.closest('.tech-btn'); if (targetButton) { handleActivation(targetButton, targetButton.dataset.ticker); } }, { passive: false }); screenerTable.addEventListener('click', (e) => { const targetButton = e.target.closest('.tech-btn'); if (targetButton) { handleActivation(targetButton, targetButton.dataset.ticker); } }); const observer = new MutationObserver(updateButtons); observer.observe(screenerTable, { childList: true, subtree: true }); updateButtons(); } } // --- Main Logic --- if (window.location.href.includes('screener.ashx')) { setupScreenerButtons(); } else { setupSingleStockButton(); } })();