You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3+TypeScript+类组件:输入框获焦时动态添加HTML元素的问题

Vue3 + TypeScript + vue-class-component 动态添加元素问题

前提

技术栈:Vue3 + TypeScript + vue-class-component
需求:输入框触发focusin事件时,向模板内的div元素中添加一个动态创建的div。

尝试的两种方法

1. 通过ID获取元素添加动态div

单组件使用时有效,但页面复用组件时,因元素ID全局唯一,动态div只会添加到第一个组件的目标div中。

代码示例:

<template>
  <div id="testId" ref="errorDiv">
    <input type="text" @focusin="handleFocusIn"/>
  </div>
</template>

<script lang="ts">
import { Vue } from 'vue-class-component';

export default class HelloWorld extends Vue {
  public handleFocusIn = (): void => {
    this.appendDivById(); // 页面复用组件时失效
  }

  private appendDivById(): void {
    let errorDivRef = document.getElementById('testId') as HTMLDivElement

    const div = document.createElement("div") as HTMLDivElement;
    div.innerHTML = "Hello World";

    errorDivRef.append(div);
  }
}
</script>

2. 通过ref获取元素添加动态div

该方法无效,$refs.errorDiv始终未定义。虽然知道ref在mounted钩子中可访问,但不符合事件触发的时机需求。

代码示例:

<template>
  <div id="testId" ref="errorDiv">
    <input type="text" @focusin="handleFocusIn"/>
  </div>
</template>

<script lang="ts">
import { Vue } from 'vue-class-component';

export default class HelloWorld extends Vue {
  declare $refs: {
    errorDiv: HTMLDivElement;
  };

  // errorDiv = ref<HTMLDivElement | null>(null); // 即使在mounted中value始终为null

  public handleFocusIn = (): void => {
    this.appendDivByRef(); // $refs.errorDiv始终未定义
  }

  public mounted() { // 此时有效但时机不符
    const div = document.createElement("div") as HTMLDivElement;
    div.innerHTML = "Hello World";

    this.$refs.errorDiv.append(div); // $refs.errorDiv未定义
  }

  private appendDivByRef(): void {
    const div = document.createElement("div") as HTMLDivElement;
    div.innerHTML = "Hello World";

    this.$refs.errorDiv.append(div); // $refs.errorDiv始终未定义
  }
}
</script>

补充说明

使用v-for生成动态列表的方案不适用,因为仅需添加一个元素。

问题

我的思路是否正确,还是这种实现方法完全错误?


解决方案

你的核心问题是直接操作DOM的方式不符合Vue组件化逻辑,且ref的使用未遵循vue-class-component的规范。以下是两种可行方案:

方案1:用响应式数据控制元素渲染(推荐)

完全无需手动操作DOM,通过响应式变量控制元素显示,这是Vue的最佳实践,天然支持组件复用:

<template>
  <div ref="errorDiv">
    <input type="text" @focusin="showDynamicDiv = true"/>
    <!-- 用v-if控制动态元素的显示/隐藏 -->
    <div v-if="showDynamicDiv">Hello World</div>
  </div>
</template>

<script lang="ts">
import { Vue } from 'vue-class-component';

export default class HelloWorld extends Vue {
  // 响应式变量控制元素渲染状态
  private showDynamicDiv = false;
}
</script>

方案2:正确使用ref手动操作DOM(若必须操作DOM)

如果一定要手动创建DOM元素,需使用vue-class-component的@Ref()装饰器正确获取组件内的ref:

<template>
  <div ref="errorDiv">
    <input type="text" @focusin="handleFocusIn"/>
  </div>
</template>

<script lang="ts">
import { Vue, Ref } from 'vue-class-component';

export default class HelloWorld extends Vue {
  // 用@Ref装饰器声明ref,自动关联模板中的ref元素
  @Ref() readonly errorDiv!: HTMLDivElement;

  public handleFocusIn = (): void => {
    // 先检查ref是否存在,避免报错
    if (!this.errorDiv) return;
    
    const div = document.createElement("div");
    div.innerHTML = "Hello World";
    div.classList.add('dynamic-div');
    
    // 避免重复添加,先检查目标元素内是否已存在该动态div
    if (!this.errorDiv.querySelector('.dynamic-div')) {
      this.errorDiv.append(div);
    }
  }
}
</script>

总结

  • 通过ID获取DOM的方法在组件复用时必然失效,因为ID是全局唯一标识,不能在复用组件中重复使用。
  • 你之前使用ref的方式错误,未遵循vue-class-component的语法规范,需用@Ref()装饰器声明ref。
  • 优先推荐响应式数据控制渲染的方案,符合Vue的设计理念,代码更简洁易维护;若必须操作DOM,需按规范使用ref并做好边界检查。

内容的提问来源于stack exchange,提问作者Dackelkotze

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 15:35:36