动态菜单点击异常求助:无法触发Import From子菜单Device...选项
动态菜单交互问题排查
我遇到一个棘手问题,折腾一整天都没解决。场景是页面里的动态菜单:鼠标悬停顶部File菜单时显示一级菜单,但子菜单不会自动渲染;必须悬停Import From菜单才能调出子菜单,还要在它动态消失前点击Device...选项。我写了下面的代码但完全没用,求帮忙找问题。
原代码
var elements = document.querySelectorAll('.geItem'); for (var i = 0; i < elements.length; i++) { if (elements[i].innerHTML === 'File') { // 创建点击事件 var event = new MouseEvent('click', { bubbles: true, cancelable: true, view: window }); (elements[i] as any).click(); setTimeout(() => { var menuItems = document.querySelectorAll('td.mxPopupMenuItem[align="left"]'); var importFromMenu; for (var j = 0; j < menuItems.length; j++) { var menuItem = menuItems[j]; if (menuItem.textContent.trim() === "Import from") { importFromMenu = menuItem; console.log('import from:'); console.log(importFromMenu); (importFromMenu as any).click() break; } } }, 2000); break; // 只点击第一个匹配的元素 } }
菜单层级示意图

页面相关HTML结构
<div class="mxPopupMenu geMenubarMenu" style="display: inline; z-index: 10006; left: 146.969px; top: 345.906px; overflow: hidden auto; max-height: 1300px;"> <table class="mxPopupMenu"> <tbody> <tr class="mxPopupMenuItem"> <td class="mxPopupMenuIcon"></td> <td class="mxPopupMenuItem" align="left">Open from</td> <td class="mxPopupMenuItem" style="padding-right: 6px; text-align: right;"><img src="data:image/gif;base64,R0lGODlhCQAJAIAAAP///zMzMyH5BAEAAAAALAAAAAAJAAkAAAIPhI8WebHsHopSOVgb26AAADs=" /></td> </tr> <tr> <td class="mxPopupMenuIcon" style="padding: 0px;"></td> <td colspan="2" style="padding: 0px;"><hr size="1" /></td> </tr> <tr class="mxPopupMenuItem"> <td class="mxPopupMenuIcon"></td> <td class="mxPopupMenuItem" align="left">Save</td> <td class="mxPopupMenuItem" style="padding-right: 6px; text-align: right;"><span style="color: gray;">Ctrl+S</span></td> </tr> <tr> <td class="mxPopupMenuIcon" style="padding: 0px;"></td> <td colspan="2" style="padding: 0px;"><hr size="1" /></td> </tr> <tr class="mxPopupMenuItem"> <td class="mxPopupMenuIcon"></td> <td class="mxPopupMenuItem" align="left">Rename...</td> <td class="mxPopupMenuItem" style="padding-right: 6px; text-align: right;"></td> </tr> <tr> <td class="mxPopupMenuIcon" style="padding: 0px;"></td> <td colspan="2" style="padding: 0px;"><hr size="1" /></td> </tr> <tr class="mxPopupMenuItem"> <td class="mxPopupMenuIcon"></td> <td class="mxPopupMenuItem" align="left">Import from</td> <td class="mxPopupMenuItem" style="padding-right: 6px; text-align: right;"><img src="data:image/gif;base64,R0lGODlhCQAJAIAAAP///zMzMyH5BAEAAAAALAAAAAAJAAkAAAIPhI8WebHsHopSOVgb26AAADs=" /></td> </tr> <tr class="mxPopupMenuItem"> <td class="mxPopupMenuIcon"></td> <td class="mxPopupMenuItem" align="left">Export as</td> <td class="mxPopupMenuItem" style="padding-right: 6px; text-align: right;"><img src="data:image/gif;base64,R0lGODlhCQAJAIAAAP///zMzMyH5BAEAAAAALAAAAAAJAAkAAAIPhI8WebHsHopSOVgb26AAADs=" /></td> </tr> <tr> <td class="mxPopupMenuIcon" style="padding: 0px;"></td> <td colspan="2" style="padding: 0px;"><hr size="1" /></td> </tr> <tr class="mxPopupMenuItem"> <td class="mxPopupMenuIcon"></td> <td class="mxPopupMenuItem" align="left">Embed</td> <td class="mxPopupMenuItem" style="padding-right: 6px; text-align: right;"><img src="data:image/gif;base64,R0lGODlhCQAJAIAAAP///zMzMyH5BAEAAAAALAAAAAAJAAkAAAIPhI8WebHsHopSOVgb26AAADs=" /></td> </tr> <tr> <td class="mxPopupMenuIcon" style="padding: 0px;"></td> <td colspan="2" style="padding: 0px;"><hr size="1" /></td> </tr> <tr class="mxPopupMenuItem"> <td class="mxPopupMenuIcon"></td> <td class="mxPopupMenuItem" align="left">New Library</td> <td class="mxPopupMenuItem" style="padding-right: 6px; text-align: right;"><img src="data:image/gif;base64,R0lGODlhCQAJAIAAAP///zMzMyH5BAEAAAAALAAAAAAJAAkAAAIPhI8WebHsHopSOVgb26AAADs=" /></td> </tr> <tr class="mxPopupMenuItem"> <td class="mxPopupMenuIcon"></td> <td class="mxPopupMenuItem" align="left">Open Library from</td> <td class="mxPopupMenuItem" style="padding-right: 6px; text-align: right;"><img src="data:image/gif;base64,R0lGODlhCQAJAIAAAP///zMzMyH5BAEAAAAALAAAAAAJAAkAAAIPhI8WebHsHopSOVgb26AAADs=" /></td> </tr> <tr> <td class="mxPopupMenuIcon" style="padding: 0px;"></td> <td colspan="2" style="padding: 0px;"><hr size="1" /></td> </tr> <tr class="mxPopupMenuItem"> <td class="mxPopupMenuIcon"></td> <td class="mxPopupMenuItem" align="left">Properties...</td> <td class="mxPopupMenuItem" style="padding-right: 6px; text-align: right;"></td> </tr> </tbody> </table> </div> <div class="mxPopupMenu" style="position: absolute; display: inline; z-index: 10006; left: 385px; top: 537px; overflow: hidden auto; max-height: 1186px;"> <table class="mxPopupMenu"> <tbody> <tr class="mxPopupMenuItem"> <td class="mxPopupMenuIcon"></td> <td class="mxPopupMenuItem" align="left">Device...</td> <td class="mxPopupMenuItem" style="padding-right: 6px; text-align: right;"></td> </tr> <tr> <td class="mxPopupMenuIcon" style="padding: 0px;"></td> <td colspan="2" style="padding: 0px;"><hr size="1" /></td> </tr> <tr class="mxPopupMenuItem"> <td class="mxPopupMenuIcon"></td> <td class="mxPopupMenuItem" align="left">Dwp Library</td> <td class="mxPopupMenuItem" style="padding-right: 6px; text-align: right;"></td> </tr> </tbody> </table> </div>
问题排查与修复方案
1. 核心错误:用点击事件替代悬停触发
菜单的展开逻辑是鼠标悬停触发,不是点击。原代码调用click()可能会触发菜单关闭或其他无关逻辑,应该模拟mouseover和mouseenter事件。
2. Import From子菜单未处理
原代码只点击了Import From,但子菜单需要悬停才会渲染,而且要等子菜单渲染完成后再点击Device...,不能跳过悬停步骤。
3. 元素选择器精度不足
原选择器会同时选中一级菜单和子菜单的元素,建议通过父元素类区分:
- 一级菜单:
.geMenubarMenu td.mxPopupMenuItem[align="left"] - 子菜单:
.mxPopupMenu:not(.geMenubarMenu) td.mxPopupMenuItem[align="left"]
4. 固定延迟不可靠
用固定setTimeout容易因为页面加载速度波动失效,改用MutationObserver监听子菜单节点变化更稳定。
修复后的完整代码
// 找到File菜单 const fileMenu = Array.from(document.querySelectorAll('.geItem')) .find(item => item.innerHTML === 'File'); if (fileMenu) { // 模拟鼠标悬停展开File菜单 const hoverEvents = [ new MouseEvent('mouseover', { bubbles: true, cancelable: true, view: window }), new MouseEvent('mouseenter', { bubbles: true, cancelable: true, view: window }) ]; hoverEvents.forEach(evt => fileMenu.dispatchEvent(evt)); // 等待一级菜单渲染 setTimeout(() => { // 找到Import from菜单 const importFromMenu = Array.from(document.querySelectorAll('.geMenubarMenu td.mxPopupMenuItem[align="left"]')) .find(item => item.textContent.trim() === "Import from"); if (importFromMenu) { // 模拟悬停展开子菜单 hoverEvents.forEach(evt => importFromMenu.dispatchEvent(evt)); // 监听子菜单出现,找到后点击Device... const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.addedNodes.length) { const deviceItem = Array.from(document.querySelectorAll('.mxPopupMenu:not(.geMenubarMenu) td.mxPopupMenuItem[align="left"]')) .find(item => item.textContent.trim() === "Device..."); if (deviceItem) { deviceItem.click(); observer.disconnect(); // 完成后停止监听 } } } }); // 监听页面节点变化,范围可以缩小到菜单容器提升性能 observer.observe(document.body, { childList: true, subtree: true }); } }, 800); // 根据实际页面渲染速度调整延迟 }
内容的提问来源于stack exchange,提问作者user10759242
相关产品推荐
相关产品推荐

