I’m creating a custom cursor by dynamically appending a div element in the useEffect hook when the component mounts and is set to follow the mouse using event listeners. However, two divs are being rendered—one properly follows the mouse, while the other stays at the top-left corner of the viewport.
import { useRef, useEffect, useState } from "react"
function Pcanvas({width, height, pixelSize}){
const [canvasColor, setCanvasColor] = useState("#dcdcdc")
const [pencilSize, setPencilSize] = useState(pixelSize)
const canvasRef = useRef(null)
var mousePressed = false
function mouseCursor(){
const cursor = document.createElement("div")
cursor.className = 'cursor'
cursor.style.backgroundColor = 'black'
cursor.style.width = pencilSize + 'px'
cursor.style.height = pencilSize + 'px'
cursor.style.position = 'absolute';
cursor.style.display = 'block';
cursor.style.zIndex = 999;
cursor.style.pointerEvents = 'none';
document.body.appendChild(cursor)
console.log(cursor.getBoundingClientRect().y)
}
function handleMouseMov(event){
const cursor = document.getElementsByClassName('cursor')[0]
console.log(cursor.getBoundingClientRect().y)
if(cursor){
cursor.style.top = event.clientY-(pencilSize/2) + 'px'
cursor.style.left = event.clientX-(pencilSize/2) + 'px'
}
}
function handleMouseLeave(event){
const cursor = document.getElementsByClassName('cursor')[0]
if(cursor){
cursor.style.display = 'none'
}
}
function handleMouseEnter(event){
const cursor = document.getElementsByClassName('cursor')[0]
if(cursor){
cursor.style.display = 'block'
}
}
useEffect(() => {
const canvas = canvasRef.current
mouseCursor()
document.body.addEventListener('mouseenter', handleMouseEnter)
document.body.addEventListener('mousemove', handleMouseMov)
document.body.addEventListener('mouseleave', handleMouseLeave)
return(() => {
document.body.removeEventListener('mouseenter', handleMouseEnter)
document.body.removeEventListener('mousemove', handleMouseMov)
document.body.removeEventListener('mouseleave', handleMouseLeave)
})
}, [pencilSize])
return(
<></>
)
}
export default Pcanvas
You should avoid createElement
and especially getElementsByClassName
when using React.
Let the component self render the div
, then use an useRef
to change any styling when needed.
const { useRef, useEffect, useState } = React;
function Pcanvas({ width, height, pixelSize = 15 }){
const cursor = useRef(null)
const [pencilSize, setPencilSize] = useState(pixelSize)
const handleMouseEnter = (event) => cursor.current.style.display = 'block';
const handleMouseLeave = (event) => cursor.current.style.display = 'none';
const handleMouseMov = (event) => {
cursor.current.style.top = event.clientY-(pencilSize/2) + 'px'
cursor.current.style.left = event.clientX-(pencilSize/2) + 'px'
}
useEffect(() => {
document.body.addEventListener('mouseenter', handleMouseEnter);
document.body.addEventListener('mousemove', handleMouseMov);
document.body.addEventListener('mouseleave', handleMouseLeave);
return(() => {
document.body.removeEventListener('mouseenter', handleMouseEnter);
document.body.removeEventListener('mousemove', handleMouseMov);
document.body.removeEventListener('mouseleave', handleMouseLeave);
})
}, [pencilSize])
return (
<div
ref={cursor}
className={'cursor'}
style={{ width: pencilSize, height: pencilSize, display: 'none' }}
/>
);
}
ReactDOM.render(<Pcanvas />, document.getElementById("react"));
html, body, #react { height: 100%; }
.cursor {
background: black;
position: absolute;
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>