56 lines
1.7 KiB
JavaScript
56 lines
1.7 KiB
JavaScript
/**
|
|
* Detects if the primary pointing device or environment is a touch device
|
|
* (e.g. tablet, phone, POS touchscreen terminal).
|
|
* Returns false on desktop devices with mice/trackpads even if they have touchscreens.
|
|
*/
|
|
export function isTouchDevice() {
|
|
if (typeof window === 'undefined') return false;
|
|
if (window.matchMedia) {
|
|
if (window.matchMedia('(pointer: coarse)').matches) {
|
|
return true;
|
|
}
|
|
if (window.matchMedia('(pointer: fine)').matches) {
|
|
return false;
|
|
}
|
|
}
|
|
return 'ontouchstart' in window || (navigator.maxTouchPoints > 0);
|
|
}
|
|
|
|
/**
|
|
* Globally disables autofocus on touch devices to prevent virtual keyboard popups.
|
|
* Physical taps on inputs still focus and open the keyboard normally.
|
|
*/
|
|
export function disableTouchAutofocus() {
|
|
if (!isTouchDevice()) return;
|
|
|
|
// 1. Intercept programmatic focus called during mount/render on autofocus elements
|
|
const originalFocus = HTMLElement.prototype.focus;
|
|
HTMLElement.prototype.focus = function (options) {
|
|
if (this.hasAttribute?.('autofocus') || this.autofocus) {
|
|
this.removeAttribute?.('autofocus');
|
|
try {
|
|
this.autofocus = false;
|
|
} catch {
|
|
// ignore
|
|
}
|
|
return;
|
|
}
|
|
|
|
const stack = new Error().stack || '';
|
|
if (stack.includes('commitMount') || stack.includes('commitHostUpdate')) {
|
|
return;
|
|
}
|
|
|
|
return originalFocus.call(this, options);
|
|
};
|
|
|
|
// 2. Prevent browser from honoring native autofocus attribute on dynamically inserted elements
|
|
const originalSetAttribute = Element.prototype.setAttribute;
|
|
Element.prototype.setAttribute = function (name, value) {
|
|
if (typeof name === 'string' && name.toLowerCase() === 'autofocus') {
|
|
return;
|
|
}
|
|
return originalSetAttribute.call(this, name, value);
|
|
};
|
|
}
|