Table of Contents

Vue @click doesn't work on an anchor tag with href present

Mastering Vue Click Events on Anchor Tags: Why @click Fails and How to Fix It As developers working with component-based frameworks like Vue, we often...

2026-08-10

Mastering Vue Click Events on Anchor Tags: Why @click Fails and How to Fix It

As developers working with component-based frameworks like Vue, we often encounter subtle but frustrating interactions between native browser behavior and framework event handling. One common hurdle involves anchor tags (<a>) where the default action (navigation via href) conflicts with custom JavaScript logic bound via @click.

This post addresses a specific scenario: how to execute a Vue method when an anchor tag is clicked, while simultaneously ensuring that the link's navigation functionality (the href) is ignored. We will dive into why standard event modifiers like .prevent or .stop might not be sufficient and provide a robust solution.

The Dilemma: Navigation vs. Interaction

The core issue you are facing stems from the fundamental behavior of HTML anchor tags. When a user clicks an <a> element, the browser's default action is to follow the link specified in its href attribute. Any JavaScript event handler attached to that element must actively intercept this default behavior to override it.

You correctly identified that simple modifiers like @click.prevent, @click.stop, and chaining them often fail when dealing with nested structures or specific DOM contexts. While .stop controls event bubbling, and .prevent stops the form submission or link navigation if it targets a standard form submission, they don't always override the native navigation initiated by an <a> tag in all Vue setups, especially when dealing with multiple layers of event propagation.

The Solution: Explicitly Calling preventDefault()

The most reliable and semantically correct way to stop a browser action (like navigating via an anchor) within a JavaScript context is by calling the native event.preventDefault() method. This explicitly tells the browser, "Do not perform the default action associated with this event."

In a Vue environment, we need to ensure that when the click event fires on the link, our handler intercepts the navigation before it occurs.

Implementing the Fix in Your Vue Code

Since you are binding the click event to an <a> tag, the fix should be applied directly within the method or the event binding itself.

In your provided structure, where you are clicking a list item (<li>) but targeting the link's behavior, we need to ensure that when the click propagates to the anchor tag, the default action is halted.

Here is how you can refactor your approach to reliably achieve your goal:

<div v-if="!hasPage(category.code)">
    <div>
      <template v-for="subcategoryList in subcategoryLists[$index]">
        <ul>
          <!-- We attach the click handler here -->
          <li v-for="subcategory in subcategoryList" 
              @click="test($event)" 
              class="clickable-item"> 
  
            <!-- The anchor tag retains its href for SEO/structure -->
            <a :href="subcategory.url" @click.stop>
              {{subcategory.label}}
            </a>
          </li>
        </ul>
      </template>
    </div>
  </div>
  

Explanation of the Fix:

  1. Focus on the Anchor Tag: The navigation logic must be controlled by the <a> tag itself. We attach an explicit @click.stop to prevent unintended bubbling issues, but the crucial part is handling the default action within the context where the link resides.
  2. The Recommended Approach (If possible): If your goal is purely to trigger a function and not navigate, consider replacing the <a> tag with a <button> element. Buttons are designed for triggering actions rather than navigation, making event handling cleaner:

    html <!-- Alternative structure using <button> --> <li v-for="subcategory in subcategoryList" @click="handleSubcategoryClick(subcategory)"> <button :data-url="subcategory.url">{{ subcategory.label }}</button> </li>

  3. If you MUST use <a> (The event.preventDefault() method): If keeping the <a> tag is an SEO requirement, you must ensure that the event listener targets the link and calls preventDefault(). Since Vue bindings can sometimes obscure this, a dedicated method is safest:

    ```javascript methods: { test(event) { // Stop the default navigation immediately upon click event.preventDefault();

    // Now execute your desired logic
      console.log('Link clicked, but navigation was prevented.');
      // Add your Google Tag Manager tracking logic here
      

    } } ```

By calling event.preventDefault() inside your handler method, you gain explicit control over the browser's default action, making your code predictable regardless of how deeply nested the HTML structure is. This pattern promotes cleaner separation of concerns, which aligns with the principles of robust architecture seen in frameworks like those offered by Laravel, where clear, intentional data flow is paramount.

Conclusion

Dealing with event conflicts between native HTML behavior and custom framework logic requires understanding the precise mechanism Vue uses to bind events and how the browser enforces its default actions. While simple modifiers often fail in complex scenarios, explicitly calling event.preventDefault() within your click handler provides the most reliable means of overriding navigation when working with anchor tags. By adopting this explicit control, you ensure that your application logic takes precedence over the browser's default behavior, resulting in predictable and maintainable code.

Stefan

Stefan

SEO engineer and Laravel developer. Building tools to help Laravel applications rank higher in search results.

Share this article

Back to Blog