0

I am trying to make this more abstract. I need to find a way to use one function for the same element and different events. I would also like to be able to pass in an element argument but I know I can't do this with a callback. This is what I have thus far:

const divElement = document.querySelector('#test');
    
divElement.addEventListener(
  'mouseenter',
  (event) => {
    divElement.classList.add('shadow');
    console.log(event);
  },
  false
);
    
divElement.addEventListener(
  'mouseleave',
  (event) => {
    divElement.classList.remove('shadow');
    console.log(event);
  },
  false
);
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
Musicman
  • 49
  • 6
  • 1
    Why not a css hover style? [example](https://codepen.io/ovdojoey/pen/BomKyZ) – Wyck Nov 09 '21 at 20:35
  • Yes, I can do that but I wanted to try it with JS. I know we should opt for CSS every time, if we can. JS is sometimes overkill. However, this is a poor example. I just wanted to make something more abstract and follow the DRY principle and use a more abstract approach. I am just learning JS for the first time. I am a backend developer. – Musicman Nov 10 '21 at 13:37

3 Answers3

2

Use CSS instead. Never use JS for what can be achieved in CSS.

#test {
  background-color: white;
  padding: 30px;
  transition: all 0.4s ease;
}

#test:hover {
  box-shadow: 0 0 10px #000;
}
<div id="test">
  Lorem ipsum dolor sit amentur.
</div>

If for some reason you didn't reveal you need it to be event-listener-based, here's what I would do:

const divElement = document.querySelector('#test');
function handleMouseAction({type}) {
  this.classList.toggle('shadow', type === 'mouseenter');
}

divElement.addEventListener('mouseenter', handleMouseAction, false);
divElement.addEventListener('mouseleave', handleMouseAction, false);
#test {
  padding: 30px;
  transition: all 0.4s ease;
}

.shadow {
  box-shadow: 0 0 10px #000;
}
<div id="test">
  Lorem ipsum dolor sit amentur.
</div>
connexo
  • 53,704
  • 14
  • 91
  • 128
  • I have a couple of questions. Why the curly brackets ```{ type }``` ? Also,, how would I pass an element into this function instead of hard coding "divElement" ? – Musicman Nov 10 '21 at 15:16
  • The `{ type } ` syntax is called object destructuring and means "from the object passed, give me the value of the `type` property of that object, as a local variable named `type`". You don't need to pass the element, you can access it using `this`. I've adjusted my second code example accordingly. – connexo Nov 10 '21 at 17:42
  • I get what you're saying. I should have guessed it would be "this" but I'm used to other languages where "this" and "self" refer to a method or property of a class. – Musicman Nov 10 '21 at 19:10
  • Event listeners passed to `addEventListener` get bound to the element the listener is attached to (unless you use an arrow function). – connexo Nov 10 '21 at 19:25
1

You can write a helper function and call that from the two callbacks:

const divElement = document.querySelector('#test');
function handleEvent(event, action) {
  divElement.classList[action]('shadow');
  console.log(event);
}

divElement.addEventListener('mouseenter', (event) => handleEvent(event, 'add'), false);
divElement.addEventListener('mouseleave', (event) => handleEvent(event, 'remove'), false);

Alternatively, you can use partial application with closures to create the two callbacks from one abstract function:

const divElement = document.querySelector('#test');
function makeEventHandler(action) {
  return (event) => {
    divElement.classList[action]('shadow');
    console.log(event);
  };
}

divElement.addEventListener('mouseenter', makeEventHandler('add'), false);
divElement.addEventListener('mouseleave', makeEventHandler('remove'), false);

Of course @Wyck is right and in this particular example, you should do everything with CSS only :-)

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • While JS allows this, it is certainly a bad programming practice. I've also coded like that in the past, but in many code reviews and discussions I dropped it because it makes code harder to refactor. – connexo Nov 09 '21 at 20:40
  • @connexo What in particular is a bad practice? I assume OP wants to learn about the techniques how to simplify event handlers, not specifically make a hover shadow. – Bergi Nov 09 '21 at 20:41
  • `classList[action]` – connexo Nov 09 '21 at 20:47
  • @connexo I don't see what's so hard to refactor about that. Another option would be `classList.toggle('shadow', flag)` with a boolean flag parameter, but I prefer a two-value string enum for clarity. – Bergi Nov 09 '21 at 20:50
  • That is what I would do, but I wouldn't pass `flag` but instead calculate it based on `event.type`. – connexo Nov 09 '21 at 21:00
  • 1
    @connexo Ah, that's a nice approach as well. Have an upvote! – Bergi Nov 09 '21 at 21:02
  • I know it's bad practice. But this was a bad example. What I was really unclear about was how to pass in the 'add' and 'remove'. I tried it like Element.classList.functionArg('shadow'); and it didn't work. But I see here that I need to surround it with []? – Musicman Nov 10 '21 at 13:43
  • The [ ] are for getting the same as . dot notation in this example. ```divElement.classList[action]('shadow');``` turns into ```divElement.classList['add']('shadow');``` Correct? – Musicman Nov 10 '21 at 19:24
  • @MattPaolini Yes, see https://stackoverflow.com/q/4968406/1048572 – Bergi Nov 10 '21 at 19:25
-1

My way to do that:

const divElement = document.querySelector('#test');
const events = ["mouseenter", "mouseleave"]

events.forEach(event => {
    divElement.addEventListener(event, (e) => {
       // TODO put logic here.
       divElement.classList.add('shadow');
       console.log(event);
   })
})

Another way to do that, by using onmouseenter="" tag and onmouseleave="" tag, and letting them use the same callback.

const callback = (element) => {
   element.classList.add("shadow");
}
.shadow {
  display: block;
  box-shadow: 0 0 10px black;
}
<div onmouseenter="callback(this)" onmouseleave="callback(this)">Hello World</div>
Nawaf Khalifah
  • 594
  • 3
  • 11