优先用 querySelectorAll 选中菜单项并取 textContent,因其返回静态快照、支持复杂选择器且 textContent 不受 CSS 隐藏影响;含图标时需过滤子节点或酌情用 innerText;注意脚本执行时机,确保 DOM 已就绪。

用 querySelectorAll 选中菜单项再取 textContent
大多数 HTML 菜单是 <ul><li> 结构,或用 <nav> 包裹带类名的链接。直接选中所有菜单项元素,再批量读取文本最稳妥。
常见错误是只用 innerText —— 它受 CSS 隐藏影响(比如 display: none 或 visibility: hidden 的项会丢),而 textContent 只看 DOM 树结构,更可靠。
实操建议:
- 优先用语义化选择器,比如
document.querySelectorAll('nav ul li a')或document.querySelectorAll('.menu-item a') - 如果菜单是纯文本
<li>首页</li>,就选li元素本身,再取textContent - 记得用
.trim()去掉首尾空白,避免换行符和缩进混入结果
处理含图标或嵌套标签的菜单项
现代菜单常在文字外包裹 <i>、<svg> 或 <span>,比如 <a href="/home"><i class="icon-home"></i> 首页</a>。直接取 a.textContent 会把图标占位符(如空格、Unicode 符号)也拉进来。
立即学习“前端免费学习笔记(深入)”;
这时不能只靠一层 textContent,得过滤掉非文本节点:
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
- 用
Array.from(el.childNodes)遍历子节点,只保留nodeType === 3(文本节点) - 或者更简单:用
el.innerText(此时它反而比textContent更干净,因为会忽略不可见元素的文本)——但仅限你确认菜单项本身没被 CSS 隐藏 - 若图标是伪元素(
::before),那它本就不在 DOM 中,textContent和innerText都不会包含它,无需额外处理
用 getElementsByClassName 还是 querySelectorAll?
getElementsByClassName 返回的是实时集合(live collection),后续 DOM 变动会自动反映在该集合里;而 querySelectorAll 返回静态快照。对提取文本这种一次性操作,两者效果一致,但 querySelectorAll 更灵活、支持复杂选择器,推荐无脑用它。
注意两个易错点:
-
getElementsByClassName('menu-item')不支持链式写法,不能写成getElementsByClassName('menu-item').getElementsByTagName('a')—— 必须先转数组或用querySelectorAll('.menu-item a') - 类名含空格会被当成多个类,比如
class="nav-item active"要写成.nav-item.active,不是.nav-item active
服务端渲染页面中脚本执行时机问题
如果菜单是 JS 动态生成的(比如 React/Vue 渲染),而你的提取脚本放在 <head> 或未加延迟,很可能取到空数组。这不是选择器写错了,是 DOM 还没出来。
解决方法取决于场景:
- 普通静态页:把脚本放
</body>前,或监听DOMContentLoaded - 单页应用(SPA):不能依赖初始 DOM,得等框架挂载完成,例如监听
window.__INITIAL_STATE__、或用MutationObserver监听菜单容器变化 - 服务端预渲染(SSR)+ 客户端激活(hydration):确保提取逻辑在 hydration 后执行,否则可能拿到服务端生成的旧结构
真正麻烦的不是怎么取文本,而是菜单到底什么时候“算准备好”——这个时间点往往比选择器语法更难判断。


















