如何编写用户脚本在网页添加按钮触发GM_registerMenuCommand功能
给Gmail LaTeX脚本添加网页内触发按钮
问题
现有Gmail LaTeX渲染脚本,仅能通过ViolentMonkey插件菜单的「Render LaTeX」按钮触发传统LaTeX分隔符的渲染逻辑。需要在Gmail页面内添加一个按钮,点击后实现相同功能,同时适配Gmail的样式。
解决步骤
提取核心逻辑为独立函数
将GM_registerMenuCommand内的匿名函数内容抽成单独的renderTraditionalLatex函数,让菜单和按钮共用同一逻辑,避免代码重复。适配Gmail样式创建按钮
使用Gmail原生按钮的CSS类名,让新按钮样式与页面原生按钮一致;将按钮插入到邮件列表上方的工具栏区域,符合用户操作习惯。确保页面元素加载完成后插入按钮
利用原脚本的waitForElement函数,等待Gmail工具栏加载完成后再插入按钮,避免因元素未加载导致的插入失败。
修改后的完整脚本
// ==UserScript== // @name LaTeX for Gmail v2.2 // @namespace Violentmonkey Scripts // @match https://mail.google.com/mail/* // @grant GM_registerMenuCommand // @grant GM_addElement // @require https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js // @version 2.2 // @author /u/MistralMireille & /u/LoganJFisher // @description Adds support for TeXTheWorld delimiters to Gmail, plus an on-page button and menu option to render traditional LaTeX delimiters // ==/UserScript== GM_addElement('link', { rel: "stylesheet", href: "https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.css" }); function renderLatex() { document.querySelectorAll("#\:1 > .nH .aHU.hx > [role='list'] > [role='listitem'][aria-expanded='true']").forEach(message => { let subportion = message.querySelector("[data-message-id]"); if(subportion) { message = subportion; } message.innerHTML = message.innerHTML.replace(/\[;(.+?);\]/g, (match, p1) => { return katex.renderToString(p1, { throwOnError: false, output: "mathml", displayMode: false }); }); message.innerHTML = message.innerHTML.replace(/\[\(;(.+?);\)\]/g, (match, p1) => { return katex.renderToString(p1, { throwOnError: false, output: "mathml", displayMode: true }); }); }); } // 提取菜单中的核心渲染逻辑为独立函数 function renderTraditionalLatex() { document.querySelectorAll("#\:1 > .nH .aHU.hx > [role='list'] > [role='listitem'][aria-expanded='true']").forEach(message => { let subportion = message.querySelector("[data-message-id]"); if(subportion) { message = subportion; } message.innerHTML = message.innerHTML.replace(/\\\[(.+?)\\\]/g, (match, p1) => { return katex.renderToString(p1, { throwOnError: false, output: "mathml", displayMode: true }); }); message.innerHTML = message.innerHTML.replace(/\$\$(.+?)\$\$/g, (match, p1) => { return katex.renderToString(p1, { throwOnError: false, output: "mathml", displayMode: true }); }); message.innerHTML = message.innerHTML.replace(/\\\((.+?)\\\)/g, (match, p1) => { return katex.renderToString(p1, { throwOnError: false, output: "mathml", displayMode: false }); }); message.innerHTML = message.innerHTML.replace(/\$(.+?)\$/g, (match, p1) => { return katex.renderToString(p1, { throwOnError: false, output: "mathml", displayMode: false }); }); message.innerHTML = message.innerHTML.replace(/\\begin\{math}(.+?)\\end\{math}/g, (match, p1) => { return katex.renderToString(p1, { throwOnError: false, output: "mathml", displayMode: false }); }); }); } GM_registerMenuCommand('Render LaTeX', renderTraditionalLatex); function waitForElement(queryString) { let count = 0; return new Promise((resolve, reject) => { let findInterval = setInterval(() => { let waitElement = document.querySelector(queryString); if(waitElement) { clearInterval(findInterval); resolve(waitElement); } else if(count > 20) { clearInterval(findInterval); reject(`Couldn't find waitElement: ${queryString}.`); } else { count += 1; } }, 100); }); } // 创建并插入适配Gmail样式的按钮 function addRenderButton() { const button = document.createElement('button'); // 使用Gmail原生按钮类名,自动适配页面样式 button.className = 'T-I J-J5-Ji L3 T-I-ax7 T-I-Js-Gs mA'; button.textContent = 'Render LaTeX'; button.style.marginLeft = '8px'; // 绑定渲染逻辑 button.addEventListener('click', renderTraditionalLatex); // 插入到邮件列表上方的工具栏区域 waitForElement('.T-I.J-J5-Ji.T-I-KE.L3').then(toolbar => { toolbar.parentNode.insertBefore(button, toolbar.nextSibling); }); } window.addEventListener('load', () => { waitForElement("#\:1 > .nH").then(messagesDiv => { (new MutationObserver((mutationRecords, observerElement) => { mutationRecords.forEach(mutationRecord => { switch(mutationRecord.type) { case "childList": mutationRecord.addedNodes.forEach(addedNode => { if(addedNode.tagName === "DIV" && addedNode.getAttribute("role") === "listitem") { renderLatex(); } }); break; case "attributes": if(mutationRecord.target.tagName === "DIV" && mutationRecord.target.getAttribute("role") === "listitem" && mutationRecord.attributeName === "aria-expanded") { renderLatex(); } } }); })).observe(messagesDiv, { childList: true, subtree: true, attributes: true, attributeOldValue: true}); // 页面加载完成后添加按钮 addRenderButton(); }); });
关键说明
- 按钮复用Gmail原生CSS类,无需手动编写大量样式即可适配页面风格
- 按钮插入位置为邮件列表上方的工具栏,符合用户日常操作路径
- 菜单命令与页面按钮共用同一渲染函数,确保逻辑一致性
- 修复了原脚本中
GM_addElement加载样式表的属性错误,将src改为href以正确加载KaTeX样式
内容的提问来源于stack exchange,提问作者Logan J. Fisher
相关产品推荐
相关产品推荐

