How to make a cursor image Hover effect with CSS & JS!
Tags: webdev, tutorial, css, javascript
Published: 2024-02-20 16:07:04

Custom cursors and cursor hover effects are a great way to impress users on your website, whether you're making an e-commerce website or a portfolio. A good example is a cursor image hover effect, where when the cursor hovers over something it shows an image, here's my way of making it! 

{% codepen https://codepen.io/lensco825/pen/BabEdBO %}

## Setting up the example
I'll make a short and simple vertical nav with the `nav` element for this example. There will be six `div` elements in it, and outside the nav is a div with the class of "cursor", that'll be our custom cursor.
```html
    <nav>
      <div>Lofi</div>
      <div>Motivation</div>
      <div>Profiles</div>
      <div>Goals</div>
      <div>Academics</div>
    </nav>
    <div class="cursor"></div>
```
Now we add the CSS, that tells the font size, the alignment, and a border on each nav item except the last one. 

```css

html, body {
    width: 99%;
    height: 99%
}

body {
    display: flex;
    align-items: center;
}

nav {
    font-size: 6rem;
    display: grid;
}

nav > div:not(div:nth-child(5)) {
    border-bottom: solid ;
}

@media (max-width: 480px) {
    nav {
        font-size: 4.8rem;
    }
}
```
<hr>
# Making and controlling the custom cursor
First, we must remove the visibility of the default cursor by setting the website's (`*`) `cursor` set to `none`. Then we create our custom cursor, which is the cursor div we made before. It'll have a `width` and `height` of `20px` and its border-radius will be set to `50%` so it becomes a circle. You can add whatever `background-color` you'd like for it, I chose `black` so it can be easily visible. The most important part is that you set it's `position` to `fixed` so the cursor always stays in view and `pointer-events` to `none` so the default actions of how a default cursor would work don't effect the custom one. Finally you set the `background-size` to `cover` and a scale `transition`, these will both be important for the hover effects later on.

```css
* {
    cursor: none;
}

.cursor {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background-color: black;
    /*Keeps our custom cursor on the user's screen*/
    position: fixed;
    pointer-events: none;
    /*makes sure the background images will sort of fit the cursor*/
    background-size: cover; 
    transition: scale 300ms;
}
```
_Note: I said the visibility of the cursor and not the cursor itself because the computer still does the actions of the cursor such as selecting text even when the cursor isn't shown_

Now in Javascript, we make a variable that stores the cursor.

```js
let cursor = document.querySelector(".cursor");
```
Next, we'll make a function called `cursorControl` with an `e` parameter that stands for "event". In it we make an X and Y variable that use `e.clientX` and `e.clientY` to find the coordinates of where the cursor should be based on the movements of the mouse. Then, we place our custom cursor based on our coordinates by setting it's top position to the `y` and left position to the `x`. Finally, we just call the function in our `mousemove` event listener.

```js
function cursorControl(e) {
//tracks the cursors coordinates
  let x = e.clientX;
  let y = e.clientY;
//places the custom cursor onto the cursor coordinates
  cursor.style.top = `${y}px`;
  cursor.style.left = `${x}px`;
}

document.addEventListener("mousemove", cursorControl);
```
There you have it! A custom cursor, now we just need to add the hover effects.

<hr>
# Cursor growth hover effect
Before we go into the Javascript part of things, we need to make a new class called `cursorGrow` with a `scale` property of `8`. This class will be added to the cursor whenever it hovers over a nav div.
```css
.cursorGrow {
scale: 8;
}
```
Now, in JS we save all of the nav divs into a variable.
```js
let divs = document.querySelectorAll("div:not(.cursor)");
```
Furthermore, we make a `forEach` method. Where for each div in our nav, when the cursor enters/hovers over them the `cursorGrow` CSS class will be added causing the scale of the cursor to go up by 8, but when the cursor leaves the class is removed and the size goes back to normal.

```js
divs.forEach((div) => {
  div.addEventListener("mouseenter", function () {
    cursor.classList.add("cursorGrow");
  });
  div.addEventListener("mouseleave", function () {
    cursor.classList.remove("cursorGrow");
  });
});
```
Now, because of what we've done so far, the code should create this (make sure to add a transition to the cursor if you hadn't already):
{% codepen https://codepen.io/lensco825/pen/abMxVpe %}
<hr>
# Cursor Image hover effect
Last but not least, we finally add the image hover effect! To add each image, we'll make a CSS classes based on each div an a corresponding image for them.

```css
/*Background image for each div based on their text*/
.lofi {
    background-image: url('https://images.unsplash.com/photo-1585477281005-b83d4b47eba4?q=80&w=1887&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');
}

.motivation {
    background-image: url('https://images.unsplash.com/photo-1556711905-4bd1b6603275?q=80&w=1887&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');
}

.webDev {
    background-image: url('https://images.unsplash.com/photo-1498050108023-c5249f4df085?q=80&w=1772&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');
}

.goals {
    background-image: url('https://i.pinimg.com/564x/4d/ee/43/4dee438cb40321df2cb0cfb14a3c7811.jpg');
}

.academics {
    background-image: url('https://i.pinimg.com/564x/f8/3d/8b/f83d8b8d17e4315caa647dab88e6addc.jpg');
}

```
Now we _could_ do this similar to our growth hover effect, where we continue to write `mouseenter` and `mouseleave` event listeners, but that would be too much code. Instead we could make a loop! But first we need to store the names of each image class in an array and the `divs` variable we had before.

```js
let divs = document.querySelectorAll("div:not(.cursor)");
const imageClassNames = ["lofi", "motivation", "webDev", "goals", "academics"];
```
Now for the loop. How it works is, in the loop there are the `mouseenter` and `mouseleave` event listeners, and they add and remove classes from the image class array. Whenever a class is added to the div, the div is given the properties of the class, which is the background image of it, but when removed the properties from the class are removed as well, that's how `classList.add()` and `classList.remove()` work.  
To make sure the right class corresponds with the right div, the loop goes from 0 to the length of the array so as long as the order in the `divs` array and the `imageClassNames` array the correct div and the correct array value always match. So, for example it'll start with the 0th value of the `divs` array which is the lofi div, and it'll add the class that's named 0th value of `imageClassNames` which is `lofi` whenever the mouse enters the div, but removes it when it _leaves_ the div. The loop does it for every value in the image class and divs array, and stops us from writing constant event listeners in our code.

```js
const ImageClassNames = ["lofi", "motivation", "webDev", "goals", "academics"];

for (let i = 0; i < divs.length; i++) {
  divs[i].addEventListener("mouseenter", () => {
    cursor.classList.add(imageClassNames[i]);
  });

  divs[i].addEventListener("mouseleave", () => {
    cursor.classList.remove(imageClassNames[i]);
  });
}

```
Now, we should finally have our cursor image hover effect!
{% codepen https://codepen.io/lensco825/pen/BabEdBO %}
<hr>
# Conclusion
That's really it! It took me a bit of trial and error to make this cursor hover effect out of my mind, but in the end, it was worth it, which is why I wanted to make a blog about it! I show more of my web dev, game development, and even 3D blender creations on my [twitter](https://twitter.com/Lensco13). Follow for more and have an awesome day/night👋.