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:
<button onclick="window.location.href='example.com'">Download</button><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?
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
- Solutions for CSS and styles
- Proper JavaScript event handling
- Comprehensive solution for a button-link
- Testing and debugging
- Additional recommendations
Main causes of the problem
The problem with the first click on Android Chrome devices occurs due to several specific features:
-
Lack of visual feedback - buttons without explicit cursor indication may not be recognized as clickable elements by the browser
-
Touch and click event conflict - mobile browsers have a delay between
touchstartandclickevents to prevent accidental presses -
CSS hover effects -
:hoverrules can block or change button behavior on mobile devices -
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
.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 browserstouch-action: manipulation;- speeds up response to touch events-webkit-user-select: none;- prevents unwanted text selection- Remove hover effects - avoid
:hoverstyles for mobile version
According to research, “try setting the CSS style of the button as
cursor: pointer;”.
Proper JavaScript event handling
Optimal handler implementation
// 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
// 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, orontouchmoveevents”.
Comprehensive solution for a button-link
HTML structure
<a href="example.com"
class="download-btn"
data-download="true"
onclick="handleDownloadClick(event, this)">
Download
</a>
JavaScript handler
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
/* 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
- Chrome DevTools Device Mode - test on various mobile devices
- Event Listeners - check which event handlers are bound to the element
- Console logging - add logs to track code execution
// 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()
// 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
// 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
- Using
<button>withformaction:
<form action="example.com" method="get">
<button type="submit" class="download-btn">Download</button>
</form>
- Dynamically creating a link:
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);
}
- Checking navigation capability:
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
textIsSelectableis set to ‘true’. Removing this attribute or setting it to ‘false’ will allow the first press to be detected”.
Sources
- onClick event is not working on Android Chrome - Stack Overflow
- Button Not Working in Android Browser in Kendo UI for jQuery - Telerik Forums
buttonclickevent not triggered on Chrome Android - GitHub- on:click event not firing when touching button on a mobile device - Reddit
- android - why my button does not work on the first click? - Stack Overflow
- Button not working on Mobile Devices but works on PC bootstrap - Stack Overflow
- 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:
-
Mandatory add
cursor: pointer;andtouch-action: manipulation;to CSS styles - this solves most button recognition problems on mobile devices -
Use a combined event handling approach -
touchstartfor mobile andclickfor desktop with navigation capability checks -
Check parent event handlers for
preventDefault()that might block navigation -
Optimize UX with visual feedback and protection against multiple clicks
-
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.