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

Vue中引入Codepen打字效果脚本失效问题及解决方案求助

Vue中实现打字效果失效的问题与解决方法

问题描述

将Codepen上的打字效果代码复制到Vue文件后,仅显示“Im A'”及闪烁光标,文字切换效果未触发,推测脚本未运行。以下是用户提供的原始代码:

原始HTML

<div class="container">
    <p>Im A'<span class="typed-text"></span><span class="cursor">&nbsp;</span></p>
</div>

原始CSS

p {
  overflow: hidden;
}
.container p span.typed-text {
  font-weight: normal;
  color: white;
  text-shadow:1px 1px 1px yellow;
}
.container p span.cursor {
  display: inline-block;
  background-color: #ccc;
  margin-left: 0.1rem;
  width: 3px;
  animation: blink 1s infinite;
}
.container p span.cursor.typing {
  animation: none;
}
@keyframes blink {
  0%  { background-color: #ccc; }
  49% { background-color: #ccc; }
  50% { background-color: transparent; }
  99% { background-color: transparent; }
  100%  { background-color: #ccc; }
}

原始JS(存在语法错误)

const typedTextSpan = document.querySelector(".typed-text");
const cursorSpan = document.querySelector(".cursor");

const textArray = ["web developer", "UI/UX Designer", "Front End"];
const typingDelay = 200;
const erasingDelay = 100;
const newTextDelay = 2000; // Delay between current and next text
let textArrayIndex = 0;
let charIndex = 0;

function type() {
  if (charIndex < textArray[textArrayIndex].length) {
    if(!cursorSpan.classList.contains("typing")) cursorSpan.classList.add("typing");
    typedTextSpan.textContent += textArray[textArrayIndex].charAt(charIndex);
    charIndex++;
    setTimeout(type, typingDelay);
  } 
  else {
    cursorSpan.classList.remove("typing");
      setTimeout(erase, newTextDelay);
  }
}

function erase() {
    if (charIndex > 0) {
    if(!cursorSpan.classList.contains("typing")) cursorSpan.classList.add("typing");
    typedTextSpan.textContent = textArray[textArrayIndex].substring(0, charIndex-1);
    charIndex--;
    setTimeout(erase, erasingDelay);
  } 
  else {
    cursorSpan.classList.remove("typing");
    textArrayIndex++;
    if(textArrayIndex>=textArray.length) textArrayIndex=0;
    setTimeout(type, typingDelay + 1100);
// 缺少函数闭合大括号

Vue与普通HTML中JS运行的核心区别

  • DOM渲染时机不同:普通HTML脚本在DOM加载完成后直接执行,而Vue组件的DOM是异步渲染的,直接用document.querySelector可能无法获取到组件内的元素(组件尚未挂载)。
  • 作用域隔离:Vue组件有独立作用域,全局脚本无法直接访问组件内DOM,且直接操作DOM不符合Vue响应式设计理念。
  • 生命周期约束:Vue组件有明确的生命周期钩子,脚本需在合适的钩子(如mounted)中执行,确保DOM已渲染完成。
  • 语法错误影响:原始JS中erase函数缺少闭合大括号,会直接导致脚本终止运行。

正确实现方法

方法一:适配Vue生命周期的原生JS改造

将脚本放入Vue组件的mounted钩子,使用组件内DOM查询替代全局查询,同时修复语法错误:

<template>
  <div class="container">
    <p>Im A'<span class="typed-text"></span><span class="cursor">&nbsp;</span></p>
  </div>
</template>

<script>
export default {
  mounted() {
    const typedTextSpan = this.$el.querySelector(".typed-text");
    const cursorSpan = this.$el.querySelector(".cursor");
    
    const textArray = ["web developer", "UI/UX Designer", "Front End"];
    const typingDelay = 200;
    const erasingDelay = 100;
    const newTextDelay = 2000;
    let textArrayIndex = 0;
    let charIndex = 0;

    function type() {
      if (charIndex < textArray[textArrayIndex].length) {
        if(!cursorSpan.classList.contains("typing")) cursorSpan.classList.add("typing");
        typedTextSpan.textContent += textArray[textArrayIndex].charAt(charIndex);
        charIndex++;
        setTimeout(type, typingDelay);
      } else {
        cursorSpan.classList.remove("typing");
        setTimeout(erase, newTextDelay);
      }
    }

    function erase() {
      if (charIndex > 0) {
        if(!cursorSpan.classList.contains("typing")) cursorSpan.classList.add("typing");
        typedTextSpan.textContent = textArray[textArrayIndex].substring(0, charIndex-1);
        charIndex--;
        setTimeout(erase, erasingDelay);
      } else {
        cursorSpan.classList.remove("typing");
        textArrayIndex++;
        if(textArrayIndex >= textArray.length) textArrayIndex = 0;
        setTimeout(type, typingDelay + 1100);
      }
    }

    // 启动打字效果
    type();

    // 组件销毁时清理定时器,避免内存泄漏
    this.$once('hook:beforeDestroy', () => {
      // 可收集定时器ID并逐一清除,简单场景可省略
    });
  }
}
</script>

<style scoped>
p {
  overflow: hidden;
}
.container p span.typed-text {
  font-weight: normal;
  color: white;
  text-shadow:1px 1px 1px yellow;
}
.container p span.cursor {
  display: inline-block;
  background-color: #ccc;
  margin-left: 0.1rem;
  width: 3px;
  animation: blink 1s infinite;
}
.container p span.cursor.typing {
  animation: none;
}
@keyframes blink {
  0%  { background-color: #ccc; }
  49% { background-color: #ccc; }
  50% { background-color: transparent; }
  99% { background-color: transparent; }
  100%  { background-color: #ccc; }
}
</style>

方法二:Vue响应式实现(更推荐)

通过Vue响应式数据控制文本内容与光标状态,符合Vue开发规范:

<template>
  <div class="container">
    <p>Im A'<span class="typed-text">{{ currentText }}</span><span :class="['cursor', { typing: isTyping }]">&nbsp;</span></p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      textArray: ["web developer", "UI/UX Designer", "Front End"],
      currentText: "",
      textIndex: 0,
      charIndex: 0,
      isTyping: false,
      typingDelay: 200,
      erasingDelay: 100,
      newTextDelay: 2000
    };
  },
  mounted() {
    this.startTyping();
  },
  methods: {
    startTyping() {
      this.type();
    },
    type() {
      this.isTyping = true;
      if (this.charIndex < this.textArray[this.textIndex].length) {
        this.currentText += this.textArray[this.textIndex].charAt(this.charIndex);
        this.charIndex++;
        setTimeout(() => this.type(), this.typingDelay);
      } else {
        this.isTyping = false;
        setTimeout(() => this.erase(), this.newTextDelay);
      }
    },
    erase() {
      this.isTyping = true;
      if (this.charIndex > 0) {
        this.currentText = this.textArray[this.textIndex].substring(0, this.charIndex - 1);
        this.charIndex--;
        setTimeout(() => this.erase(), this.erasingDelay);
      } else {
        this.isTyping = false;
        this.textIndex = (this.textIndex + 1) % this.textArray.length;
        setTimeout(() => this.type(), this.typingDelay + 1100);
      }
    }
  },
  beforeDestroy() {
    // 清理所有定时器,避免内存泄漏
    const timerCount = setTimeout(() => {}, 0);
    for (let i = 0; i < timerCount; i++) {
      clearTimeout(i);
    }
  }
};
</script>

<style scoped>
p {
  overflow: hidden;
}
.container p span.typed-text {
  font-weight: normal;
  color: white;
  text-shadow:1px 1px 1px yellow;
}
.container p span.cursor {
  display: inline-block;
  background-color: #ccc;
  margin-left: 0.1rem;
  width: 3px;
  animation: blink 1s infinite;
}
.container p span.cursor.typing {
  animation: none;
}
@keyframes blink {
  0%  { background-color: #ccc; }
  49% { background-color: #ccc; }
  50% { background-color: transparent; }
  99% { background-color: transparent; }
  100%  { background-color: #ccc; }
}
</style>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 12:00:59