
本文介绍一种无需正则表达式的稳健方法,通过遍历 DOM 文本节点识别 @media(...) 指令,并自动关联其后紧邻的 HTML 元素,实现语义化媒体规则绑定与结构化数据提取。
本文介绍一种无需正则表达式的稳健方法,通过遍历 dom 文本节点识别 `@media(...)` 指令,并自动关联其后紧邻的 html 元素,实现语义化媒体规则绑定与结构化数据提取。
在构建轻量级模板引擎或响应式样式注入系统时,常需将类似 @media(770) hide 这类内联媒体指令与对应 DOM 元素建立映射关系。传统做法(如对 innerHTML 使用正则匹配)易受换行、空格、注释或嵌套结构干扰,且无法准确反映真实 DOM 树中的父子/兄弟关系。更可靠的方式是直接操作 DOM 节点树——聚焦于文本节点(nodeType === 3),识别以 @media 开头的纯文本内容,并安全获取其后首个非空白元素节点。
以下为推荐实现方案:
/**
* 提取所有 @media 指令及其关联的下一个元素节点
* @returns {Array<[string, Element]>} 每项为 [指令字符串, 关联元素]
*/
function extractMediaRules(root = document.body) {
const results = [];
function walk(node) {
for (const child of node.childNodes) {
// 仅处理文本节点
if (child.nodeType === Node.TEXT_NODE) {
const text = child.nodeValue.trim();
if (text.startsWith('@media(')) {
// 查找下一个非空、非文本节点(即目标元素)
let nextEl = child.nextSibling;
while (nextEl && (nextEl.nodeType !== Node.ELEMENT_NODE || nextEl.nodeName === 'SCRIPT')) {
nextEl = nextEl.nextSibling;
}
if (nextEl instanceof Element) {
results.push([text, nextEl]);
}
}
} else if (child.nodeType === Node.ELEMENT_NODE && child.hasChildNodes()) {
walk(child); // 递归遍历子元素
}
}
}
walk(root);
return results;
}
// 使用示例
const mediaEntries = extractMediaRules();
console.log(mediaEntries);
// 输出类似:
// [
// ['@media(1080) mmw-400 mmh-300', <article>],
// ['@media(770) mmw-300', <article>],
// ['@media(770) hide', <time>]
// ]✅ 关键优势说明:
- ✅ 精准定位:基于 DOM 树真实结构,不受 HTML 字符串格式(缩进、换行、注释)影响;
- ✅ 健壮容错:跳过 SCRIPT、注释节点及空白文本节点,避免误匹配;
- ✅ 零依赖:纯原生 JavaScript,无正则复杂度与潜在回溯风险;
- ✅ 可扩展性强:返回原始指令字符串,便于后续解析(如提取断点值、类名列表)。
? 注意事项:
- @media 指令必须位于目标元素之前且在同一父容器内(符合 HTML 流式布局逻辑);
- 若存在多个连续文本节点(如因 DOM 操作动态插入),建议先调用 parent.normalize() 合并相邻文本节点;
- 对于 或 Shadow DOM 内容,需显式传入对应根节点(如 extractMediaRules(template.content))。
最后,将原始数组转换为你所需的嵌套对象结构,只需一次归约即可:
const mediaMap = mediaEntries.reduce((acc, [rule, el]) => {
const match = rule.match(/@media\((\d+)\)\s+(.+)/);
if (!match) return acc;
const [, breakpoint, classListStr] = match;
const classes = classListStr.trim().split(/\s+/).filter(Boolean);
const key = el.tagName; // 或使用 el.id / el.className 做唯一标识
if (!acc[key]) acc[key] = {};
acc[key][breakpoint] = { classes };
return acc;
}, {});
console.log(mediaMap);
// → { ARTICLE: { "1080": {classes: [...]}, "770": {...} }, TIME: { "770": {...} } }该方案兼顾可读性、可维护性与运行时稳定性,是实现声明式响应式模板逻辑的理想基础。










