Can an Anchor link open an Accordion?

I set an anchor link ID on text inside of an Accordion. Is there a way to get the Accordion to open when the link is clicked?

See this thread: Linking to Anchor in Closed Accordion?

I have javascript (courtesy of ChatGPT) that does this as part of a DevPack that creates a table of text entries from a CSV file. You could potentially include the code below as part of a custom HTML component.

I think I have stripped out the relevant code but haven’t tested it:

@portal(headEnd)
<style>
.accordion-highlight {
  animation: pulseHighlight 2s ease-in-out;
  background-color: yellow;
  /* Optional: smoother edges */
  border-radius: 4px;
}

@keyframes pulseHighlight {
  0%, 100% {
    background-color: yellow;
    box-shadow: 0 0 8px 3px rgba(255, 255, 0, 0.7);
  }
  50% {
    background-color: #fffacd; /* lighter yellow */
    box-shadow: 0 0 12px 6px rgba(255, 255, 0, 1);
  }
}
</style>

@endportal


@portal(bodyEnd)
<script>
  function highlightElement(target) {
    const highlightTarget = target.closest("p") || target;
    highlightTarget.classList.add("accordion-highlight");
    setTimeout(() => {
      highlightTarget.classList.remove("accordion-highlight");
    }, 3000);
  }

  function handleAccordionAnchor(hash) {
    if (!hash || !hash.startsWith('#')) return;

    // console.log("[Accordion Scroll] Hash triggered:", hash);

    const target = document.querySelector(hash);
    if (!target) {
      // console.log("[Accordion Scroll] Target not found.");
      return;
    }

    // console.log("[Accordion Scroll] Target element found:", target);

    // Find the accordion wrapper containing the target
    const accordionWrapper = target.closest('[aria-roledescription="accordion"]');
    if (!accordionWrapper) {
      console.log("[Accordion Scroll] Accordion wrapper not found.");
      return;
    }

    // console.log("[Accordion Scroll] Accordion wrapper found:", accordionWrapper);

    const alpineComponent = Alpine && Alpine.$data(accordionWrapper);
    if (!alpineComponent) {
      console.log("[Accordion Scroll] Alpine component not found.");
      return;
    }

    // console.log("[Accordion Scroll] Alpine component found:", alpineComponent);

    if (!alpineComponent.open) {
      // console.log("[Accordion Scroll] Accordion is closed. Opening...");
      alpineComponent.open = true;

      setTimeout(() => {
        // console.log("[Accordion Scroll] Scrolling to anchor...");
        target.scrollIntoView({ behavior: "smooth", block: "start" });

      highlightElement(target);
      }, 350);
    } else {
      // console.log("[Accordion Scroll] Accordion already open. Scrolling...");
      target.scrollIntoView({ behavior: "smooth", block: "start" });

      highlightElement(target);
    }
  }

  // 🔁 Handle in-page link clicks (including repeated clicks)
  document.addEventListener("click", (e) => {
    const anchor = e.target.closest('a[href^="#"]');
    if (!anchor) return;

    const hash = anchor.getAttribute("href");
    if (hash === window.location.hash) {
      handleAccordionAnchor(hash); // Handle even if hash hasn't changed
    }
  });

  // 🔁 Handle URL hash changes
  window.addEventListener("hashchange", () => {
    handleAccordionAnchor(window.location.hash);
  });

  // 🔁 Handle initial page load with hash
  window.addEventListener("DOMContentLoaded", () => {
    if (window.location.hash) {
      setTimeout(() => {
        handleAccordionAnchor(window.location.hash);
      }, 200); // Small delay to let Alpine initialize
    }
  });
</script>
@endportal

Just copy the above code and paste it into the custom HTML component and hopefully it will work. It is searching for a paragraph of text nearest the target anchor.

I would add the custom HTML component once at the start of the page but it doesn’t really matter as it is using @portal commands to insert the code at the end of the heading section and at the end of the body section.

When you click on an anchor, it will (Should) open the accordion and scroll to the text related to the target anchor and flash the text in yellow for 3 seconds (the value 3000 in the code).

As @ben said in the linked thread, there is a risk that if the Elements core code changes than this might stop working.

Once the Market Place is up and running this could be packaged up as a DevPack and have properties so that it could be configured e.g. the display time (3 seconds) and colours etc.

Of course, it (or something similar) could be incorporated into the Elements core. :wink:

Thanks @logrunner. This is above my pay grade! Interesting non the less.