Skip to content

Commit 8b30cd6

Browse files
committed
🐛 Prioritize interactive elements and ignore mobile users
1 parent be5e311 commit 8b30cd6

File tree

1 file changed

+58
-5
lines changed

1 file changed

+58
-5
lines changed

src/index.tsx

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,17 @@ import React, { useState, useEffect } from 'react';
44
import { motion, Variants } from 'motion/react';
55
import { CursorType, Position, CursorProps } from './types';
66

7-
const INTERACTIVE_ELEMENTS: string[] = ['BUTTON', 'A', 'INPUT', 'LABEL', 'SELECT', 'TEXTAREA', 'DETAILS', 'SUMMARY'];
7+
const INTERACTIVE_ELEMENTS: string[] = [
8+
'BUTTON',
9+
'A',
10+
'INPUT',
11+
'LABEL',
12+
'SELECT',
13+
'TEXTAREA',
14+
'DETAILS',
15+
'SUMMARY',
16+
'OPTION',
17+
];
818

919
const TEXT_ELEMENTS: string[] = [
1020
'P',
@@ -30,8 +40,44 @@ const TEXT_ELEMENTS: string[] = [
3040
'MARK',
3141
'Q',
3242
'CITE',
43+
'DL',
44+
'DT',
45+
'DD',
46+
'FIGCAPTION',
47+
'ABBR',
48+
'TIME',
49+
'DEL',
50+
'INS',
51+
'VAR',
52+
'SAMP',
53+
'KBD',
54+
'SMALL',
3355
];
3456

57+
const isMobile = (): boolean => {
58+
return /iPhone|iPad|iPod|Android|WebOS/i.test(navigator.userAgent);
59+
};
60+
61+
const isInteractive = (target: HTMLElement): boolean => {
62+
if (INTERACTIVE_ELEMENTS.includes(target.tagName)) {
63+
return true;
64+
}
65+
// Check interactions via a role or an 'onclick' event
66+
if (
67+
target.getAttribute('role') === 'button' ||
68+
target.getAttribute('role') === 'link' ||
69+
target.hasAttribute('onclick')
70+
) {
71+
return true;
72+
}
73+
// Check if the element belongs to an interactive container
74+
if (target.closest('A') || target.closest('BUTTON')) {
75+
return true;
76+
}
77+
78+
return false;
79+
};
80+
3581
const Cursor: React.FC<CursorProps> = ({ zIndex = 9999 }) => {
3682
const [position, setPosition] = useState<Position>({ x: 0, y: 0 });
3783
const [cursorType, setCursorType] = useState<CursorType>('default');
@@ -77,6 +123,10 @@ const Cursor: React.FC<CursorProps> = ({ zIndex = 9999 }) => {
77123
};
78124

79125
useEffect(() => {
126+
if (isMobile()) {
127+
return;
128+
}
129+
80130
const handleMouseMove = (e: MouseEvent): void => {
81131
setPosition({ x: e.clientX, y: e.clientY });
82132
};
@@ -86,14 +136,13 @@ const Cursor: React.FC<CursorProps> = ({ zIndex = 9999 }) => {
86136

87137
if (target.hasAttribute('disabled')) {
88138
setCursorType('disabled');
89-
} else if (INTERACTIVE_ELEMENTS.includes(target.tagName) || (target.tagName === 'IMG' && target.closest('A'))) {
139+
}
140+
// Prioritize interactive elements
141+
else if (isInteractive(target)) {
90142
setCursorType('hover');
91143
} else if (TEXT_ELEMENTS.includes(target.tagName)) {
92144
setFontSize(parseFloat(getComputedStyle(target).fontSize));
93145
setCursorType('text');
94-
} else if (target.tagName === 'A' && target.querySelector('IMG')) {
95-
// fix strange css behavior of images inside links
96-
setCursorType('default');
97146
} else {
98147
setCursorType('default');
99148
}
@@ -118,6 +167,10 @@ const Cursor: React.FC<CursorProps> = ({ zIndex = 9999 }) => {
118167
};
119168
}, []);
120169

170+
if (isMobile()) {
171+
return null;
172+
}
173+
121174
return (
122175
<motion.div
123176
className="custom-cursor"

0 commit comments

Comments
 (0)