NeuroAgent

Download Button Not Working on Android: Solution

Fix for download button issues in Android Chrome. Why the first click doesn't work and how to fix custom button-links for mobile devices.

Question

When clicking on a custom ‘Download’ button, the navigation doesn’t occur even though the JavaScript handler executes. How can I resolve this?

I’m encountering strange behavior with a custom download button. I have an element that visually looks like a <button> but is implemented as an <a> tag with styles.

The problem:

  • It works correctly on desktop
  • On mobile devices (Android Chrome), navigation sometimes doesn’t happen - only after the second tap

Attempted solutions:

  1. <button onclick="window.location.href='example.com'">Download</button>
  2. <a href="example.com" class="btn" onclick="return true;">Download</a>

I suspect the issue might be with preventDefault() somewhere higher up the DOM tree or with touch event specifics.

How should I properly implement a clickable ‘button-link’ that:

  • Works on all mobile browsers
  • Maintains button styling
  • Isn’t blocked by parent JavaScript event handlers?
NeuroAgent

When clicking on a custom ‘Download’ button, navigation doesn’t occur on Android Chrome devices, usually this is related to touch event handling and CSS style features. To solve the problem, you need to add cursor: pointer; to the button’s styles, check for preventDefault() in parent event handlers, and consider using touchstart events instead of the standard onclick.

Contents

Main causes of the problem

The problem with the first click on Android Chrome devices occurs due to several specific features:

  1. Lack of visual feedback - buttons without explicit cursor indication may not be recognized as clickable elements by the browser

  2. Touch and click event conflict - mobile browsers have a delay between touchstart and click events to prevent accidental presses

  3. CSS hover effects - :hover rules can block or change button behavior on mobile devices

  4. preventDefault() blocking - parent event handlers may call preventDefault() without checking the target element

As noted in StackOverflow research, “remove any hover rules or CSS rules for the button and check if it works”.

Solutions for CSS and styles

The most effective CSS solutions to fix the problem:

Basic styles for a button-link

css
.download-btn {
    display: inline-block;
    padding: 12px 24px;
    background-color: #007bff;
    color: white;
    text-decoration: none;
    border-radius: 4px;
    font-size: 16px;
    font-weight: 500;
    cursor: pointer;  /* Critical for mobile devices */
    -webkit-user-select: none;
    user-select: none;
    touch-action: manipulation;  /* Optimization for touch devices */
}

Important CSS rules

  • cursor: pointer; - mandatory property for mobile browsers
  • touch-action: manipulation; - speeds up response to touch events
  • -webkit-user-select: none; - prevents unwanted text selection
  • Remove hover effects - avoid :hover styles for mobile version

According to research, “try setting the CSS style of the button as cursor: pointer;”.

Proper JavaScript event handling

Optimal handler implementation

javascript
// Option 1: Using touchstart for mobile
document.querySelector('.download-btn').addEventListener('touchstart', function(e) {
    e.preventDefault();  // Prevent default behavior
    window.location.href = this.href;
}, { passive: false });

// Option 2: Combined approach for all devices
document.querySelector('.download-btn').addEventListener('click', function(e) {
    if (this.href && !this.getAttribute('data-no-navigate')) {
        window.location.href = this.href;
    }
});

Checking parent handlers

javascript
// Check if parent is blocking with preventDefault()
document.addEventListener('click', function(e) {
    if (e.target.matches('.download-btn')) {
        console.log('Download button clicked');
        // Additional logic can be added here
    }
}, true);  // Use capturing phase for early detection

As explained in Reddit discussions, “to handle events on mobile devices, you should use ontouchstart, ontouchend, or ontouchmove events”.

Comprehensive solution for a button-link

HTML structure

html
<a href="example.com" 
   class="download-btn"
   data-download="true"
   onclick="handleDownloadClick(event, this)">
   Download
</a>

JavaScript handler

javascript
function handleDownloadClick(event, element) {
    // Check if this is really our button
    if (!element || !element.hasAttribute('data-download')) {
        return true;
    }
    
    // For mobile devices, use touchstart
    if ('ontouchstart' in window) {
        event.preventDefault();
        // Add a small delay for better UX
        setTimeout(() => {
            window.location.href = element.href;
        }, 100);
    } else {
        // For desktop - standard navigation
        window.location.href = element.href;
    }
    
    return false;  // Prevent further processing
}

Responsive CSS styles

css
/* Basic styles */
.download-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 12px 24px;
    background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
    color: white;
    text-decoration: none;
    border-radius: 6px;
    font-size: 16px;
    font-weight: 600;
    min-width: 120px;
    min-height: 48px;  /* Minimum height for touch targets */
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    transition: all 0.2s ease;
}

/* Active state for mobile */
.download-btn:active {
    transform: scale(0.98);
    background: linear-gradient(135deg, #0056b3 0%, #004085 100%);
}

Testing and debugging

Diagnostic tools

  1. Chrome DevTools Device Mode - test on various mobile devices
  2. Event Listeners - check which event handlers are bound to the element
  3. Console logging - add logs to track code execution
javascript
// Debug handler
function debugClickHandler(event) {
    console.log('Click event:', {
        target: event.target,
        currentTarget: event.currentTarget,
        isTrusted: event.isTrusted,
        timestamp: Date.now(),
        href: event.currentTarget.href
    });
}

// Temporarily add debug handler
document.querySelector('.download-btn').addEventListener('click', debugClickHandler);

Checking preventDefault()

javascript
// Check for preventDefault() in the call chain
function checkPreventDefault(e) {
    let element = e.target;
    while (element) {
        const listeners = getEventListeners(element);
        if (listeners && listeners.click) {
            listeners.click.forEach(listener => {
                if (listener.listener.toString().includes('preventDefault')) {
                    console.warn('preventDefault found on element:', element);
                }
            });
        }
        element = element.parentElement;
    }
}

document.addEventListener('click', checkPreventDefault);

Additional recommendations

Performance optimization

javascript
// Debounce function to prevent multiple clicks
function debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
        const later = () => {
            clearTimeout(timeout);
            func(...args);
        };
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
    };
}

// Apply debounce to handler
const downloadHandler = debounce((url) => {
    window.location.href = url;
}, 300);

document.querySelector('.download-btn').addEventListener('click', (e) => {
    downloadHandler(e.currentTarget.href);
});

Alternative approaches

  1. Using <button> with formaction:
html
<form action="example.com" method="get">
    <button type="submit" class="download-btn">Download</button>
</form>
  1. Dynamically creating a link:
javascript
function triggerDownload(url) {
    const link = document.createElement('a');
    link.href = url;
    link.style.display = 'none';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}
  1. Checking navigation capability:
javascript
function isNavigationAllowed(element) {
    // Check for blocking attributes
    if (element.hasAttribute('disabled')) return false;
    if (element.hasAttribute('data-no-navigate')) return false;
    
    // Check if href exists
    if (!element.href) return false;
    
    // Check if it's an external link with blocking
    try {
        const url = new URL(element.href);
        if (url.origin !== window.location.origin && 
            element.hasAttribute('data-external-block')) {
            return false;
        }
    } catch (e) {
        return false;
    }
    
    return true;
}

It’s important to remember, as noted in research, “if you encounter this problem with TextView, the cause might be that textIsSelectable is set to ‘true’. Removing this attribute or setting it to ‘false’ will allow the first press to be detected”.

Sources

  1. onClick event is not working on Android Chrome - Stack Overflow
  2. Button Not Working in Android Browser in Kendo UI for jQuery - Telerik Forums
  3. buttonclick event not triggered on Chrome Android - GitHub
  4. on:click event not firing when touching button on a mobile device - Reddit
  5. android - why my button does not work on the first click? - Stack Overflow
  6. Button not working on Mobile Devices but works on PC bootstrap - Stack Overflow
  7. Javascript on click works on desktop but not on mobile - Treehouse Community

Conclusion

To solve the problem with clicks on a custom ‘Download’ button in Android Chrome, you need to:

  1. Mandatory add cursor: pointer; and touch-action: manipulation; to CSS styles - this solves most button recognition problems on mobile devices

  2. Use a combined event handling approach - touchstart for mobile and click for desktop with navigation capability checks

  3. Check parent event handlers for preventDefault() that might block navigation

  4. Optimize UX with visual feedback and protection against multiple clicks

  5. Test on various mobile devices using Chrome developer tools

Applying these approaches will ensure the button-link works correctly on all mobile browsers while maintaining button styling and avoiding conflicts with parent event handlers.