在TypeScript中创建可扩展的自定义DOM选择器
在前端开发中,我们经常需要根据特定规则查找DOM元素,原生的document.querySelector和document.querySelectorAll虽然能满足大部分基础场景,但在复杂业务中,自定义DOM选择器可以让逻辑更清晰、扩展性更强。本文将介绍如何在TypeScript中实现一个可扩展的自定义DOM选择器,支持灵活的规则配置和后续功能扩展。
基础选择器实现
首先我们先实现一个最基础的选择器,支持传入CSS选择器字符串,返回匹配到的DOM元素集合,同时提供类型约束,避免运行时出现类型错误。
// 定义选择器返回结果的类型,支持单个元素或元素数组
type SelectorResult = HTMLElement | HTMLElement[] | null;
// 基础选择器类
class BaseSelector {
// 存储选择器的配置项,后续扩展时可以往这里添加属性
private config: { enableLogging: boolean };
constructor(config?: { enableLogging?: boolean }) {
this.config = {
enableLogging: config?.enableLogging || false
};
}
// 核心选择方法,支持单个和多个元素查询
select(selector: string, isMultiple: boolean = false): SelectorResult {
if (this.config.enableLogging) {
console.log(`执行选择器查询,选择器为:${selector},是否查询多个:${isMultiple}`);
}
try {
if (isMultiple) {
const elements = document.querySelectorAll(selector);
// 转换为数组返回,方便后续操作
return Array.from(elements) as HTMLElement[];
} else {
return document.querySelector(selector) as HTMLElement | null;
}
} catch (error) {
console.error(`选择器查询失败,选择器为:${selector},错误信息:`, error);
return isMultiple ? [] : null;
}
}
}
// 使用示例
const baseSelector = new BaseSelector({ enableLogging: true });
// 查询单个元素
const singleDiv = baseSelector.select('.container');
// 查询多个元素
const allButtons = baseSelector.select('button', true);上面的基础选择器已经实现了核心的查询功能,同时通过config预留了配置入口,后续扩展功能时只需要修改配置项和对应逻辑即可,不需要大幅调整原有代码结构。
扩展自定义规则支持
原生选择器只支持CSS标准语法,我们可以给自定义选择器添加自定义规则解析能力,比如支持按元素属性包含特定字符串、按元素层级关系过滤等场景。
// 定义自定义规则的类型
type CustomRule = {
// 规则名称,用于标识规则类型
name: string;
// 规则执行函数,接收当前元素和规则参数,返回是否符合规则
validate: (element: HTMLElement, param: any) => boolean;
};
// 扩展后的选择器类
class ExtensibleSelector extends BaseSelector {
// 存储已注册的自定义规则
private customRules: Map<string, CustomRule>;
constructor(config?: { enableLogging?: boolean }) {
super(config);
this.customRules = new Map();
}
// 注册自定义规则
registerRule(rule: CustomRule): void {
if (this.customRules.has(rule.name)) {
console.warn(`规则 ${rule.name} 已存在,将被覆盖`);
}
this.customRules.set(rule.name, rule);
}
// 支持自定义规则的选择方法
selectWithRules(selector: string, rules: { name: string; param: any }[] = [], isMultiple: boolean = false): SelectorResult {
// 先通过原生选择器获取初始元素集合
let initialElements: HTMLElement[];
if (isMultiple) {
const elements = document.querySelectorAll(selector);
initialElements = Array.from(elements) as HTMLElement[];
} else {
const element = document.querySelector(selector) as HTMLElement | null;
initialElements = element ? [element] : [];
}
// 如果没有自定义规则,直接返回初始结果
if (rules.length === 0) {
return isMultiple ? initialElements : (initialElements[0] || null);
}
// 遍历所有自定义规则,过滤符合要求的元素
const validElements = initialElements.filter(element => {
return rules.every(ruleConfig => {
const rule = this.customRules.get(ruleConfig.name);
if (!rule) {
console.warn(`未找到名为 ${ruleConfig.name} 的自定义规则`);
return false;
}
return rule.validate(element, ruleConfig.param);
});
});
return isMultiple ? validElements : (validElements[0] || null);
}
}
// 使用示例:注册一个按属性包含字符串过滤的规则
const extensibleSelector = new ExtensibleSelector({ enableLogging: true });
extensibleSelector.registerRule({
name: 'attrContains',
validate: (element, param: { attr: string; value: string }) => {
const attrValue = element.getAttribute(param.attr);
return attrValue ? attrValue.includes(param.value) : false;
}
});
// 查询所有class包含'btn'的button元素
const targetButtons = extensibleSelector.selectWithRules('button', [
{ name: 'attrContains', param: { attr: 'class', value: 'btn' } }
], true);通过自定义规则的注册机制,我们可以在不修改选择器核心逻辑的前提下,按需添加新的过滤规则,比如后续如果需要支持按元素内容过滤、按元素可见性过滤,只需要新增对应的规则并注册即可,完全符合开闭原则。
实际场景应用示例
下面我们看一个实际业务场景的例子:在表单页面中,我们需要获取所有必填且未填写内容的输入框,原生选择器无法直接实现这个需求,用我们的自定义选择器可以很方便地完成。
// 注册表单相关的自定义规则
extensibleSelector.registerRule({
name: 'isRequired',
validate: (element) => {
// 判断元素是否有required属性,或者class包含'required'
return element.hasAttribute('required') || element.classList.contains('required');
}
});
extensibleSelector.registerRule({
name: 'isEmptyInput',
validate: (element) => {
// 只处理input和textarea元素
if (!(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement)) {
return false;
}
// 判断值是否为空
return !element.value.trim();
}
});
// 查询所有必填且未填写的输入框
const emptyRequiredInputs = extensibleSelector.selectWithRules('input, textarea', [
{ name: 'isRequired', param: null },
{ name: 'isEmptyInput', param: null }
], true);
console.log('未填写的必填输入框数量:', emptyRequiredInputs.length);这种实现方式把不同的过滤逻辑拆分成独立的规则,后续如果业务需要调整判断逻辑,只需要修改对应规则的validate函数,不会影响到其他功能的正常运行,维护成本更低。
总结
在TypeScript中实现可扩展的自定义DOM选择器,核心思路是拆分核心查询逻辑和扩展规则逻辑,通过配置项、规则注册等机制预留扩展入口。相比原生选择器,自定义选择器可以适配更复杂的业务场景,同时类型约束也能减少开发过程中的类型错误,提升代码的健壮性和可维护性。如果后续有新的查询需求,只需要按照规则规范新增对应的逻辑即可,不需要频繁改动原有代码结构。
TypeScriptDOM选择器自定义规则前端开发可扩展设计 本作品最后修改时间:2026-05-22 14:21:23