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

Vue.js中展开评论导致页面滚动到顶部的问题

Vue.js中展开评论导致页面滚动到顶部的问题

嘿,我仔细看了你的代码和问题描述,一下子就找到问题根源了——这事儿跟你克隆链接时丢失事件绑定有关!

问题本质

你在关闭其他展开的评论时,用了readMoreLink.cloneNode(true)来添加“more”链接,但cloneNode(true)只会复制元素的DOM结构和属性,不会复制你通过addEventListener绑定的事件监听。所以当你重新点击这个克隆出来的“more”链接时,event.preventDefault()根本没起作用,浏览器就会默认跳转到#(也就是页面顶部),这就是为什么只有重新展开之前关闭过的评论才会触发滚动问题。

快速修复(适合暂时不想重构的场景)

别再克隆带事件的元素了,把创建链接和绑定事件的逻辑抽成一个复用函数,每次需要添加链接时都调用这个函数,确保事件绑定正确:

function createReadMoreLink(message, truncated) {
  const readMoreLink = document.createElement("a");
  readMoreLink.href = "#";
  readMoreLink.textContent = "more";
  readMoreLink.classList.add("read-more-link");

  readMoreLink.addEventListener("click", (event) => {
    event.preventDefault();
    // 复用你原来的点击逻辑
    if (currentlyExpandedReview && currentlyExpandedReview !== message) {
      currentlyExpandedReview.classList.remove("expanded");
      const truncatedText = truncateText(
        currentlyExpandedReview.dataset.fullReview || "",
        100
      );
      currentlyExpandedReview.textContent = truncatedText;
      currentlyExpandedReview.appendChild(createReadMoreLink(currentlyExpandedReview, truncatedText));
    }

    if (message.classList.contains("expanded")) {
      message.classList.remove("expanded");
      message.textContent = truncated;
      message.appendChild(createReadMoreLink(message, truncated));
      currentlyExpandedReview = null;
    } else {
      message.classList.add("expanded");
      message.textContent = message.dataset.fullReview || message.textContent;
      // 这里也要重新创建readLessLink并绑定事件,避免同样的问题
      const readLessLink = createReadLessLink(message, truncated);
      message.appendChild(readLessLink);
      currentlyExpandedReview = message;
    }
  });

  return readMoreLink;
}

function createReadLessLink(message, truncated) {
  const readLessLink = document.createElement("a");
  readLessLink.href = "#";
  readLessLink.textContent = "...less";
  readLessLink.classList.add("read-less-link");

  readLessLink.addEventListener("click", (event) => {
    event.preventDefault();
    message.classList.remove("expanded");
    message.textContent = truncated;
    message.appendChild(createReadMoreLink(message, truncated));
    currentlyExpandedReview = null;
  });

  return readLessLink;
}

// 原来的循环里改成这样调用
if (message.textContent && message.textContent.length > 100) {
  // ...其他逻辑
  message.appendChild(createReadMoreLink(message, truncated));
}

更推荐的Vue式解决方案(符合最佳实践)

其实你现在的写法是纯DOM操作,完全没用到Vue的数据驱动特性,这在Vue项目里很容易出问题。不如改用响应式数据来管理每个评论的展开状态,彻底告别手动操作DOM的麻烦:

<template>
  <div class="reviews-container">
    <div 
      v-for="(review, index) in customerReviews" 
      :key="review.id" 
      class="review-message"
    >
      <p>
        <!-- 根据展开状态切换显示内容 -->
        {{ isExpanded[index] ? review.fullText : truncateText(review.fullText, 100) }}
        <!-- 根据文本长度和状态显示对应链接 -->
        <a 
          v-if="review.fullText.length > 100"
          @click="toggleReview(index)"
          :class="{ 'read-more-link': !isExpanded[index], 'read-less-link': isExpanded[index] }"
        >
          {{ isExpanded[index] ? "...less" : "more" }}
        </a>
      </p>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 假设你的评论数据来自接口,每个评论带唯一id和完整文本
      customerReviews: [
        { id: 1, fullText: "这里是一条超过100字的测试评论,用来验证展开收起功能是否正常工作,看看点击more和less的时候页面会不会莫名其妙滚到顶部..." },
        { id: 2, fullText: "另一条测试评论,长度刚好不够100字,所以不会显示展开链接,这样可以对比效果..." }
      ],
      isExpanded: [] // 存储每个评论的展开状态,默认未展开
    };
  },
  methods: {
    truncateText(text, maxLength) {
      if (text.length <= maxLength) return text;
      return text.slice(0, maxLength) + "...";
    },
    toggleReview(index) {
      // 切换当前评论状态,同时关闭其他所有展开的评论
      this.isExpanded = this.isExpanded.map((_, i) => i === index ? !this.isExpanded[index] : false);
    }
  },
  mounted() {
    // 初始化展开状态数组,长度和评论数一致
    this.isExpanded = new Array(this.customerReviews.length).fill(false);
  }
};
</script>

这种写法的优势非常明显:

  • 完全由数据驱动,不需要手动操作DOM、绑定事件,从根源上避免了事件丢失、DOM不同步的问题
  • 代码更简洁易读,后续维护成本低
  • 天然符合Vue的设计理念,不会触发浏览器的默认跳转行为

总结

你原来的问题是DOM操作时的事件绑定丢失,导致href="#"的默认跳转行为生效。改用Vue的响应式状态管理就能彻底解决这个问题,同时让代码更规范。如果暂时不想重构,用抽函数的方式重新创建带事件的链接也能快速修复。

备注:内容来源于stack exchange,提问作者sofies

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 13:49:34