
本文详解如何通过时间戳校验、元素存在性判断和 dom 可见性检测,精准控制 tampermonkey 脚本仅执行一次关键操作,避免因页面动态加载导致的重复触发与无限刷新问题。
本文详解如何通过时间戳校验、元素存在性判断和 dom 可见性检测,精准控制 tampermonkey 脚本仅执行一次关键操作,避免因页面动态加载导致的重复触发与无限刷新问题。
在工厂级 PLEX 状态监控场景中,Tampermonkey 脚本常用于自动化点击导航菜单(如 fltShop_Screen_Key_Link)和选择目标屏幕(如 tr_3 行)。但原始脚本未做执行防护,一旦页面因 AJAX 刷新、SPA 路由切换或 iframe 重载而重新注入脚本,就会反复触发 .click(),造成恶性循环式刷新。
根本原因并非脚本逻辑错误,而是 Tampermonkey 默认在每次 DOM 就绪(document-idle)时执行匹配脚本——而状态页往往通过局部刷新更新内容,触发多次脚本重入。解决核心在于:让脚本具备“一次性执行”能力。以下是三种经生产验证的可靠方案:
✅ 方案一:基于 localStorage 的时间锁(推荐)
通过记录上一次执行时间戳,设定最小冷却期(如 8 小时),确保同一设备上脚本不会高频重试:
// ==UserScript==
// @name Status Screen Auto-Loader (Safe)
// @namespace http://tampermonkey.net/
// @version 2024-06-15
// @description Safely load status screen once per session/day
// @match */PlantFloorVisual.aspx
// @grant none
// ==/UserScript==
const LAST_RUN_KEY = 'statusScreenAutoLoadLastRun';
const COOLDOWN_MS = 8 * 60 * 60 * 1000; // 8 hours
const lastRun = parseInt(localStorage.getItem(LAST_RUN_KEY) || '0', 10);
const now = Date.now();
if (now - lastRun > COOLDOWN_MS) {
// 执行关键操作
const menuBtn = document.getElementById('fltShop_Screen_Key_Link');
const screenRow = document.getElementById('tr_3');
if (menuBtn && !screenRow) {
menuBtn.click();
setTimeout(() => {
const targetRow = document.getElementById('tr_3');
if (targetRow) targetRow.click();
}, 1200); // 延迟略长于页面渲染预期时间
}
// 更新时间戳(仅在成功触发后写入)
localStorage.setItem(LAST_RUN_KEY, now.toString());
}
⚠️ 注意:localStorage 是全局持久化的,适合长期免干预运行;若需每次开机重置,可改用 sessionStorage。
✅ 方案二:基于目标元素存在性的防御性判断
利用页面状态变化特征——当 #tr_3 元素已存在(尤其处于可见状态),说明状态屏已加载完成,无需再触发:
// 更健壮的检测:不仅检查元素是否存在,还确认其是否可见
function isElementVisible(el) {
return el && el.offsetParent !== null && getComputedStyle(el).display !== 'none';
}
const menuBtn = document.getElementById('fltShop_Screen_Key_Link');
const screenRow = document.getElementById('tr_3');
// 仅当菜单按钮存在且目标行未加载时才操作
if (menuBtn && !isElementVisible(screenRow)) {
menuBtn.click();
setTimeout(() => {
const freshRow = document.getElementById('tr_3');
if (freshRow && !isElementVisible(freshRow)) {
freshRow.click(); // 确保点击发生在可见前(典型下拉展开逻辑)
}
}, 1000);
}
✅ 方案三:结合 MutationObserver 的智能监听(进阶)
若页面采用动态渲染(如 Vue/React 组件挂载),可监听 DOM 变化,在目标节点插入后自动终止脚本:
const observer = new MutationObserver(() => {
if (document.getElementById('tr_3')) {
observer.disconnect(); // 一旦检测到目标元素,立即停监
return;
}
});
observer.observe(document.body, { childList: true, subtree: true });
? 最佳实践总结
- 永远优先使用 document.getElementById + offsetParent 可见性检测,比单纯 null 判断更可靠;
- 避免无条件 setTimeout:应配合 if 条件包裹,防止延迟执行时目标已存在;
-
生产环境务必添加异常捕获:
try { menuBtn?.click(); } catch (e) { console.warn('[StatusLoader] Click failed:', e); } - 若页面启用 CSP(Content-Security-Policy),确保 @grant none 满足策略要求;否则需声明 @grant unsafeWindow 并谨慎使用。
通过上述任一方案,即可彻底终结“脚本自循环刷新”问题,实现稳定、静默、一次到位的状态屏自动化加载。











