JS脚本 - 批量给所有指定标签追加Class属性
- 前言
- 一. 脚本
- 二. 测试运行
前言
公司里我们有个应用引入了UBT埋点,记录了页面上所有的点击操作以及对应的点击按钮。但是我们看下来发现,我们需要给每个按钮加一个唯一标识做区分,并且这个ID是给UBT埋点专用的。那怎么办呢?
- 加
Id
: 不合理,因为如果某个标签已经自带Id
了,我们不太可能去覆盖它。 - 加
Class
:合理,class
属性可以加多个,也可以过滤。
一. 脚本
const fs = require('fs');
const path = require('path');
// 读取文件内容
const stack = [];
const classRegExp = /class="([^"]*)"/;
const idRegExp = /id="([^"]*)"/;
const args = process.argv.slice(2)
if (!args || args.length < 4) {
console.log('参数不合法')
}
const TagMapping = [{
Tag: 'a',
ATagPre: '<a',
ATagSuffix: '</a>'
}, {
Tag: 'el-button',
ATagPre: '<el-button',
ATagSuffix: '</el-button>'
}, {
Tag: 'el-dialog',
ATagPre: '<el-dialog',
ATagSuffix: '</el-dialog>'
}, {
Tag: 'button',
ATagPre: '<button',
ATagSuffix: '</button>'
}]
const currentTagRegExp = new RegExp(`(<${'a'}\\s)([^>]*)(>)`, 'i');
function contentAddUbtClass(filePath, tag, classPre) {
const tagInfo = TagMapping.filter(item => item.Tag === tag)[0];
if (!tagInfo) {
return;
}
const content = fs.readFileSync(filePath, 'utf8');
let resultContent = ''
const preLen = tagInfo.ATagPre.length;
const stuffixLen = tagInfo.ATagSuffix.length;
for (let i = 0; i < content.length; i++) {
// a标签开始
if (i + preLen < content.length && content.substring(i, i + preLen) === tagInfo.ATagPre) {
stack.push(i);
} else if (i + stuffixLen < content.length && content.substring(i, i + stuffixLen) === tagInfo.ATagSuffix) {
if (stack.length > 0) {
// 出栈,找到起始索引位置
const start = stack.pop();
// 栈为空的时候,才开始截取并替换,这决定了代码取的标签内容都是最外层的结构
if (stack.length === 0) {
// a 标签
const aTagHtml = content.substring(start, i + stuffixLen);
const newHtml = addClass(aTagHtml, i, tag, classPre);
resultContent += newHtml;
i = i + stuffixLen - 1;
}
}
} else {
// 栈为空的时候,说明这个时候还没有识别到对应标签,可以添加其他内容。若识别到了对应标签,那么对应标签包裹的内容在这里不会添加
if (stack.length === 0) {
resultContent += content.charAt(i);
}
}
}
fs.writeFileSync(filePath, resultContent, 'utf8');
}
function addClass(content, index, tag, classPre) {
// 匹配结果
const classMatch = content.match(classRegExp);
const idMatch = content.match(idRegExp);
// class属性值
let className;
if (content.includes(classPre)) {
return content;
}
if (idMatch) {
// 如果匹配到了id属性,则在原有的class属性值上添加新的样式名称
className = `${classPre}${tag}_` + idMatch[1];
// 如果没有匹配到class属性,则在原有的文本中添加新的class属性
if (classMatch) {
// 追加class
className = classMatch[1] + ` ${className}`;
}
} else if (classMatch) {
// 如果没有匹配到id属性,但匹配到了class属性,则在原有的class属性值上添加新的样式名称
className = classMatch[1] + ` ${classPre}${tag}_${index}`;
} else {
// 如果没有匹配到id属性和class属性,则生成一个新的class属性值
className = `${classPre}${tag}_${index}`;
// 在原有的文本中添加新的class属性
// content = content.replace(currentClassRegExp, `<$1 class="${className}"`);
}
// 替换原有的class属性值
return content.replace(currentTagRegExp, (match, p1, p2, p3) => {
// 判断标签原本是否包含了class属性
if (p2.includes('class=')) {
return `${p1}${p2.replace(classRegExp, `class="${className}"`)}${p3}`;
} else {
return `${p1}${p2} class="${className}"${p3}`;
}
});
}
// 遍历指定目录下的所有文件
function traverseDirectory(dirPath, extension, tag, classPre) {
// 读取目录下的所有文件和子目录
const files = fs.readdirSync(dirPath);
// 遍历每个文件和子目录
files.forEach((file) => {
// 获取文件的完整路径
const filePath = path.join(dirPath, file);
// 判断文件是否是目录
if (fs.statSync(filePath).isDirectory()) {
// 如果是目录,则递归
traverseDirectory(filePath, extension, tag, classPre);
} else {
// 如果是文件,则判断文件后缀名是否匹配指定的后缀名
if (extension.includes(path.extname(filePath))) {
// 如果匹配,则输出文件路径
contentAddUbtClass(filePath, tag, classPre)
}
}
});
}
traverseDirectory(args[0], args[1], args[2], args[3])
二. 测试运行
假设,我要给页面所有的a
标签,添加一个class
属性,固定的前缀是UBT_
,准备一个html
文件:
执行脚本:
node .\test.js './' '.html' 'a' 'UBT_'
node 脚本名称 [路径] [匹配的文件后缀(支持多个)] [匹配的标签名称] [增加的class前缀名]
结果如下:
大家也可以在这个脚本的基础上自行二创。