Expanding Reviews in Vue.js Causes Page to Scroll to the Top

I’m experiencing an issue with a Vue.js component that displays customer reviews. The functionality to expand and collapse individual reviews mostly works, but there’s a problem when revisiting a previously expanded review.

When I perform the following actions:

  1. Expand Review 1 (click “Read Full Review”).
  2. Collapse Review 1.
  3. Expand Review 2.
  4. Go back and expand Review 1 again.

Instead of expanding Review 1 as expected, the page scrolls to the top.

let currentlyExpandedReview: HTMLElement | null = null;

reviewMessages.forEach((message) => {
  const truncated = truncateText(message.textContent || "", 100);

  if (message.textContent && message.textContent.length > 100) {
    message.dataset.fullReview = message.textContent;
    message.textContent = truncated;

    const readMoreLink = document.createElement("a");
    readMoreLink.href = "#";
    readMoreLink.textContent = "more";
    readMoreLink.classList.add("read-more-link");

    const readLessLink = document.createElement("a");
    readLessLink.href = "#";
    readLessLink.textContent = "...less";
    readLessLink.classList.add("read-less-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(readMoreLink.cloneNode(true));
      }

      if (message.classList.contains("expanded")) {
        message.classList.remove("expanded");
        message.textContent = truncated;
        message.appendChild(readMoreLink);
        currentlyExpandedReview = null;
      } else {
        message.classList.add("expanded");
        message.textContent =
          message.dataset.fullReview || message.textContent;
        message.appendChild(readLessLink);
        currentlyExpandedReview = message;
      }
    });

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

    message.appendChild(readMoreLink);
  }
});

Observations

  • The issue occurs only when re-expanding a previously expanded review.
  • The page scrolls to the top instead of toggling the review.

Steps to Reproduce

  1. Expand and collapse a review.
  2. Expand another review.
  3. Go back and expand the first review again.

Expected Behavior

The first review should expand properly without scrolling the page to the top.

Actual Behavior

The page scrolls to the top instead of expanding the review.

Potential Cause

The issue might be related to the href="#" on the links, causing the browser to scroll to the top by default.

What I Tried

  • Changing href="#" to href="" but that refreshes the entire page.

How can I fix this issue to ensure smooth expansion and collapsing of reviews without the page scrolling to the top? Are there best practices for managing such toggle states in Vue.js with dynamically added event listeners?