
本文介绍一种无需正则表达式、基于 dom 树遍历的安全方式,精准提取嵌入在 html 文本节点中的 `@media(x)` 指令及其紧邻的后续元素(如 `
在自定义模板引擎或响应式样式预处理器中,常需将类似 @media(770) hide 这样的指令“绑定”到其后最近的 HTML 元素上(如
✅ 正确思路是:遍历真实 DOM 节点树,识别文本节点(nodeType === 3)中以 @media 开头的内容,并取其 nextSibling 作为目标元素——这保证了语义上的“紧邻绑定”,且完全规避 HTML 字符串解析风险。
以下为健壮、可复用的核心遍历函数:
/**
* 提取所有 @media(...) 指令及其绑定的 DOM 元素
* @returns {Array<[string, Element]>} 每项为 [mediaDirective, targetElement]
*/
function extractMediaBindings(root = document.body) {
const bindings = [];
function walk(node) {
for (const child of node.childNodes) {
if (child.nodeType === Node.TEXT_NODE) {
const text = child.nodeValue.trim();
if (text.startsWith('@media(')) {
const next = child.nextSibling;
// 确保 nextSibling 是有效元素节点(跳过空白文本、注释等)
if (next && next.nodeType === Node.ELEMENT_NODE) {
bindings.push([text, next]);
}
}
} else if (child.hasChildNodes()) {
walk(child);
}
}
}
walk(root);
return bindings;
}
// 使用示例
const bindings = extractMediaBindings();
console.log(bindings);
// 输出形如:[ ['@media(1080) mmw-400 mmh-300', ], ['@media(770) hide', ⚠️ 注意事项:
- nextSibling 可能为空或非元素节点:HTML 中 @media 后若存在换行、空格或注释,nextSibling 可能是空白文本节点(#text)或注释节点(#comment)。因此务必校验 next.nodeType === Node.ELEMENT_NODE。
- 不支持跨行绑定:本方案仅处理 @media 与目标元素处于同一父容器内且 @media 为直接前驱文本节点的场景(符合你提供的 HTML 结构)。若需支持更复杂语法(如多行、嵌套注释),建议改用编译时解析器而非运行时 DOM 遍历。
- 性能友好:仅一次深度优先遍历,时间复杂度 O(n),远优于反复 querySelectorAll('*') + 正则匹配。
? 最终结构化转换(供参考):
你可基于 extractMediaBindings() 返回结果,进一步解析每条指令并归类到目标元素下:
function buildMediaMap(bindings) {
const map = new Map(); // WeakMap 更佳:key=Element, value=mediaConfig
for (const [directive, el] of bindings) {
// 解析 @media(770) hide → { media: '770', classes: ['hide'] }
const match = directive.match(/@media\((\d+)\)\s+(.+)/);
if (!match) continue;
const [, media, classStr] = match;
const classes = classStr.trim().split(/\s+/).filter(Boolean);
if (!map.has(el)) map.set(el, {});
map.get(el)[media] = { classes };
}
return Object.fromEntries(map);
}
// 输出即为你期望的嵌套结构
console.log(buildMediaMap(bindings));该方案简洁、可靠、符合 Web 标准,是处理“指令式内联媒体查询”绑定问题的专业实践。










