导读:本期聚焦于小伙伴创作的《如何用TypeScript封装可扩展的DOM自定义选择器与方法?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何用TypeScript封装可扩展的DOM自定义选择器与方法?》有用,将其分享出去将是对创作者最好的鼓励。

使用TypeScript为DOM元素创建可扩展的自定义选择器与方法

在前端开发中,原生的DOM选择器(如document.querySelectordocument.querySelectorAll)已经能满足大部分基础需求,但在复杂项目中,我们常常需要重复编写相似的选择逻辑,或者需要针对特定业务场景定制选择规则。使用TypeScript封装一套可扩展的自定义选择器与方法,既能提升代码复用性,又能借助类型系统减少运行时错误,非常适合中大型项目的开发维护。

核心设计思路

自定义选择器体系的核心目标是:统一选择入口、支持规则扩展、保证类型安全。我们可以将选择逻辑拆解为三个部分:

  • 基础选择器:封装原生DOM选择能力,提供类型安全的返回值
  • 规则注册器:允许业务方动态添加自定义选择规则,无需修改核心代码
  • 扩展方法:基于选择器封装常用的DOM操作逻辑,减少重复代码

基础选择器实现

首先我们定义基础的选择器类型,确保选择操作返回的元素类型符合预期,避免后续使用时出现类型错误。

// 定义选择器选项类型
interface SelectorOptions {
  // 是否在找不到元素时抛出错误,默认false
  throwIfNotFound?: boolean;
  // 选择器的额外上下文,默认是document
  context?: Document | HTMLElement;
}

// 基础选择器类
class CustomSelector {
  /**
   * 选择单个DOM元素
   * @param selector 选择器字符串,支持原生CSS选择器或自定义规则前缀(如@data-id=xxx)
   * @param options 选择配置项
   * @returns 选中的元素或null
   */
  static query<T extends HTMLElement = HTMLElement>(
    selector: string,
    options: SelectorOptions = {}
  ): T | null {
    const { throwIfNotFound = false, context = document } = options;

    // 优先匹配自定义规则
    const customElement = this.matchCustomRule<T>(selector, context);
    if (customElement !== undefined) {
      return customElement;
    }

    // 匹配原生选择器
    const element = context.querySelector<T>(selector);
    if (!element && throwIfNotFound) {
      throw new Error(`未找到匹配选择器「${selector}」的元素`);
    }
    return element;
  }

  /**
   * 选择多个DOM元素
   * @param selector 选择器字符串
   * @param options 选择配置项
   * @returns 选中的元素数组
   */
  static queryAll<T extends HTMLElement = HTMLElement>(
    selector: string,
    options: SelectorOptions = {}
  ): T[] {
    const { context = document } = options;

    // 优先匹配自定义规则
    const customElements = this.matchCustomRuleAll<T>(selector, context);
    if (customElements !== undefined) {
      return customElements;
    }

    // 匹配原生选择器
    const elements = Array.from(context.querySelectorAll<T>(selector));
    return elements;
  }

  // 自定义规则匹配逻辑,后续补充
  private static matchCustomRule<T extends HTMLElement>(
    selector: string,
    context: Document | HTMLElement
  ): T | null | undefined {
    return undefined;
  }

  // 自定义规则批量匹配逻辑,后续补充
  private static matchCustomRuleAll<T extends HTMLElement>(
    selector: string,
    context: Document | HTMLElement
  ): T[] | undefined {
    return undefined;
  }
}

自定义规则扩展机制

为了让选择器具备可扩展性,我们需要实现一个规则注册器,允许开发者动态添加自定义选择规则,比如按data-id属性选择、按元素自定义属性选择等场景。

// 自定义规则类型定义
type CustomRuleMatcher = (
  selector: string,
  context: Document | HTMLElement
) => HTMLElement | HTMLElement[] | null | undefined;

class SelectorRuleRegistry {
  // 存储单个元素匹配规则,key为规则前缀
  private static singleRules: Map<string, CustomRuleMatcher> = new Map();
  // 存储多个元素匹配规则,key为规则前缀
  private static multipleRules: Map<string, CustomRuleMatcher> = new Map();

  /**
   * 注册单个元素匹配规则
   * @param prefix 规则前缀,比如@data-id,选择时写@data-id=123就会匹配该规则
   * @param matcher 匹配函数
   */
  static registerSingleRule(prefix: string, matcher: CustomRuleMatcher): void {
    this.singleRules.set(prefix, matcher);
  }

  /**
   * 注册多个元素匹配规则
   * @param prefix 规则前缀
   * @param matcher 匹配函数
   */
  static registerMultipleRule(prefix: string, matcher: CustomRuleMatcher): void {
    this.multipleRules.set(prefix, matcher);
  }

  /**
   * 匹配单个元素的自定义规则
   */
  static matchSingle(
    selector: string,
    context: Document | HTMLElement
  ): HTMLElement | null | undefined {
    for (const [prefix, matcher] of this.singleRules.entries()) {
      if (selector.startsWith(prefix)) {
        const result = matcher(selector, context);
        if (result instanceof HTMLElement) {
          return result;
        }
        return undefined;
      }
    }
    return undefined;
  }

  /**
   * 匹配多个元素的自定义规则
   */
  static matchMultiple(
    selector: string,
    context: Document | HTMLElement
  ): HTMLElement[] | undefined {
    for (const [prefix, matcher] of this.multipleRules.entries()) {
      if (selector.startsWith(prefix)) {
        const result = matcher(selector, context);
        if (Array.isArray(result)) {
          return result;
        }
        return undefined;
      }
    }
    return undefined;
  }
}

接下来我们补全CustomSelector中的自定义规则匹配逻辑,对接规则注册器:

class CustomSelector {
  // ... 之前的代码保持不变

  private static matchCustomRule<T extends HTMLElement>(
    selector: string,
    context: Document | HTMLElement
  ): T | null | undefined {
    const result = SelectorRuleRegistry.matchSingle(selector, context);
    if (result !== undefined) {
      return result as T;
    }
    return undefined;
  }

  private static matchCustomRuleAll<T extends HTMLElement>(
    selector: string,
    context: Document | HTMLElement
  ): T[] | undefined {
    const result = SelectorRuleRegistry.matchMultiple(selector, context);
    if (result !== undefined) {
      return result as T[];
    }
    return undefined;
  }
}

注册常用自定义规则示例

我们可以预注册一些常用的自定义规则,减少业务中的重复配置。比如按data-id属性选择元素的规则:

// 注册按data-id选择单个元素的规则,使用前缀 @data-id=
SelectorRuleRegistry.registerSingleRule('@data-id=', (selector, context) => {
  // 提取选择器中的data-id值,比如@data-id=123,提取出123
  const idValue = selector.replace('@data-id=', '');
  if (!idValue) return null;
  // 在上下文中查找[data-id="xxx"]的元素
  return context.querySelector(`[data-id="${idValue}"]`);
});

// 注册按data-id选择多个元素的规则
SelectorRuleRegistry.registerMultipleRule('@data-id-all=', (selector, context) => {
  const idValue = selector.replace('@data-id-all=', '');
  if (!idValue) return [];
  return Array.from(context.querySelectorAll(`[data-id="${idValue}"]`));
});

// 注册按自定义属性data-role选择元素的规则,前缀@role=
SelectorRuleRegistry.registerMultipleRule('@role=', (selector, context) => {
  const roleValue = selector.replace('@role=', '');
  if (!roleValue) return [];
  return Array.from(context.querySelectorAll(`[data-role="${roleValue}"]`));
});

扩展常用DOM操作方法

基于自定义选择器,我们可以进一步封装常用的DOM操作方法,让业务代码更简洁。比如封装显示/隐藏元素、获取表单值等方法:

class DOMUtils {
  /**
   * 显示指定选择器的元素
   * @param selector 选择器字符串
   * @param options 选择配置
   */
  static show(
    selector: string,
    options?: SelectorOptions
  ): void {
    const element = CustomSelector.query(selector, options);
    if (element) {
      element.style.display = '';
    }
  }

  /**
   * 隐藏指定选择器的元素
   * @param selector 选择器字符串
   * @param options 选择配置
   */
  static hide(
    selector: string,
    options?: SelectorOptions
  ): void {
    const element = CustomSelector.query(selector, options);
    if (element) {
      element.style.display = 'none';
    }
  }

  /**
   * 获取表单元素的值,支持input、select、textarea
   * @param selector 选择器字符串
   * @param options 选择配置
   * @returns 表单值或null
   */
  static getFormValue(
    selector: string,
    options?: SelectorOptions
  ): string | string[] | null {
    const element = CustomSelector.query(selector, options);
    if (!element) return null;

    // 处理input元素
    if (element instanceof HTMLInputElement) {
      if (element.type === 'checkbox' || element.type === 'radio') {
        return element.checked ? element.value : null;
      }
      return element.value;
    }

    // 处理select元素
    if (element instanceof HTMLSelectElement) {
      if (element.multiple) {
        return Array.from(element.selectedOptions).map(opt => opt.value);
      }
      return element.value;
    }

    // 处理textarea元素
    if (element instanceof HTMLTextAreaElement) {
      return element.value;
    }

    return null;
  }
}

实际使用示例

完成上述封装后,业务中使用自定义选择器和扩展方法会非常简洁,同时具备类型提示:

// 使用原生选择器选择元素
const header = CustomSelector.query<HTMLElement>('.page-header');
if (header) {
  header.style.backgroundColor = '#f5f5f5';
}

// 使用自定义规则选择元素,按data-id选择
const userCard = CustomSelector.query('@data-id=1001', { throwIfNotFound: true });
if (userCard) {
  console.log('找到用户卡片', userCard);
}

// 使用自定义规则批量选择元素
const allButtons = CustomSelector.queryAll('@role=btn');
allButtons.forEach(btn => {
  btn.addEventListener('click', () => console.log('按钮被点击'));
});

// 使用扩展方法操作元素
DOMUtils.hide('@data-id=loading-mask');
DOMUtils.show('.content-wrapper');

// 获取表单值
const username = DOMUtils.getFormValue('input[name="username"]');
console.log('用户名:', username);

方案优势总结

  • 可扩展性强:通过规则注册器可以随时添加新的选择规则,无需修改核心选择器代码,符合开闭原则
  • 类型安全:TypeScript的类型定义让选择操作的返回值类型明确,减少类型断言和运行时错误
  • 代码复用:统一的入口和扩展方法避免了重复的选择逻辑和DOM操作代码,降低维护成本
  • 兼容性好:自定义规则优先匹配,原生选择器作为兜底,完全兼容现有原生选择逻辑,迁移成本低

TypeScriptDOM选择器自定义选择器前端开发可扩展封装 本作品最后修改时间:2026-05-22 14:22:45

免责声明:网站部分内容来源于网络或由用户自行发表,内容观点不代表本站立场。本站是个人网站免费分享,内容仅供个人学习、研究或参考使用,如内容中引用了第三方作品,其版权归原作者所有。若内容触犯了您的权益,请联系我们进行处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。前端、网络、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握网站开发与运维所需的核心技术栈。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端逻辑,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。