1

HTML

<a class="level-item like-icon">
    <div class="icon is-medium">
        <i class="far fa-heart" onmouseover="change(true)" onmouseout="change(false)"></i>
    </div>
</a>

JS

change = (state) => {
    state
        ? event.currentTarget.setAttribute('data-prefix', 'fas')
        : event.currentTarget.setAttribute('data-prefix', 'far');
};

Goal:
To change icon (class, or in this case, attribute) when someone hovers over the icon and revert it back when the user hovers out of it. The above code seems to work but there are a couple issues. 1) It fires way to many times when I hover over it and 2) Many times, it doesn't change the attribute back to "far" (state = false). I tried attaching those events to <a> instead of <li> but the issues persist.

p.s. NOT using JQUERY

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
007
  • 2,136
  • 4
  • 26
  • 46
  • how is changing the data-prefix supposed to edit the class name? – Jhecht Apr 06 '19 at 20:30
  • `event.currentTarget` "always refers to the element to which the event handler has been attached" [@MDN](https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget). You are not setting eventListeners and assigning handlers but calling a function directly from the HTML Element `onmouse?over/on` attribute. Therefore, there's no `event` to find the `currentTarget` of. – Brian Peacock Apr 06 '19 at 20:49
  • @Jhecht I was originally going to update the class name but since I'm using font-awesome svg icons here, the class name is actually the value of data-prefix attrib so going with that. BrianPeacock trying eventListener way now. Thank you – 007 Apr 06 '19 at 21:18

4 Answers4

2

Something like this one ?

Here i adding and removing a class hover, but ti also may be any attribute or something else

window.addEventListener('mousemove', e => {
  let hovered = document.querySelector('.hover');
  if (e.target === hovered) return;
  if (hovered) { 
    console.log('mouse out from', hovered);
    hovered.classList.remove('hover');
  }
  if (!e.target.classList.contains('icon')) 
    return;
  e.target.classList.add('hover');
  console.log('mouse over on', e.target)
})
.icon {
  display: inline-block;
  width: 50px;
  height: 50px;
  transition: 100ms;
  border: solid;
  text-align: center;
  line-height: 50px;
}

.hover {
   color: red;
   border-radius: 30%;
   transform: rotate(10deg)
}
<div class="icon">1</div>
<div class="icon">2</div>
<div class="icon">3</div>
<div class="icon">4</div>
<div class="icon">5</div>
<div class="icon">6</div>
<div class="icon">7</div>
Stranger in the Q
  • 3,668
  • 2
  • 21
  • 26
  • Thank you for this interesting approach. I'll try it out and see how it works out with my scenario. – 007 Apr 06 '19 at 21:09
  • @007 i was added console log recenly to show where actions is happen – Stranger in the Q Apr 06 '19 at 21:11
  • Just tried it on my. It fires event as long as the mouse is moving on the screen. Not sure if this will be a viable solution. It seems like because the eventlistener is created at the window level, it's firing everytime the mouse moves on the screen. – 007 Apr 06 '19 at 21:23
1

This sounds like a duplicate of How do I simulate a mouseover in pure JavaScript that activates the CSS ":hover"?

It's not ideal to deal with mouseover in pure JS, but here is a working example (insipired by an answer to the post I linked).

var element = document.getElementById('hoverIcon');
element.addEventListener('mouseover', function() {
  console.log('Mouse over, set Font Awesome class here');
});

element.addEventListener('mouseout', function() {
  console.log('Mouse out, remove Font Awesome class here');
});

var event = new MouseEvent('mouseover', {
  'view': window,
  'bubbles': true,
  'cancelable': true
});
<a class="level-item like-icon">
    <div class="icon is-medium">
        <i id="hoverIcon" class="far fa-heart">ICON</i>
    </div>
</a>
Hacène
  • 253
  • 1
  • 8
  • Interesting. I see that you're targeting by ID. I've many of these "" elements and it should only fire the action for the one that's hovered over. Is there a way to manage that? – 007 Apr 06 '19 at 21:05
  • @007 I posted a second answer answering this, the action triggered is different for each of the two icons, if you have any issue adapting it to what you are trying to implement let me know – Hacène Apr 06 '19 at 21:33
  • @007 this is my second answer: https://stackoverflow.com/a/55553658/4463734 – Hacène Apr 06 '19 at 21:37
1

There are 7 'onmouse...' events...

onmousedown, 
onmouseenter, 
onmouseleave, 
onmousemove, 
onmouseout, 
onmouseover, 
onmouseup

... so it is important to use the right one for the job.

In the example clicking and mouse movement within the Element doesn't apply - all we want is a function to be called once when the mouse enters and the element and once agian when the mouse leaves. Therefore...

<!-- HTML -->

<a class="level-item like-icon">
    <div class="icon is-medium">
        <i class="far fa-heart" 
           onmouseenter="change(this)"
           onmouseleave="change(this)"></i>
    </div>
</a>

So here it seems sensible to use the onmouseenter and onmouseleave attributes to call the change() function, and in this case to passes the HTML Element under the mouse as an argument via the this keywords.

Now the function can scrutinize the element and check if it has the desired and required 'data-prefix' attribute, and if so what that attribute is set to. We can then use this condition to set/reset the 'data-prefix' attribute's value...

/* JavaScript */

change = (elem) => {
    // does the Element have a 'data-prefix' attribute
    // and what is it set to?
    let isSet = elem.hasAttribute("data-prefix") && (
        "far" === elem.getAttribute("data-prefix")
    );
    elem.setAttribute("data-prefix", (isSet ? "fas" : "far"));
}

However, as has already been mentioned using the Element.addEventListener() method is more robust and flexible than relying on HTML attributes like onmouse....

Brian Peacock
  • 1,801
  • 16
  • 24
  • Thanks for the (detailed) explanation and a solution. Can you please take a look at https://codepen.io/anon/pen/oOLrjq and see what I'm doing wrong (event listener approach)? – 007 Apr 06 '19 at 21:58
1

Here is a second version of my initial answer, this time with multiple elements.

var elements = document.getElementsByClassName("hover-icon");
var i;
for (i = 0; i < elements.length; i++) {
 element = elements[i];
  element.addEventListener('mouseover', function(data) {
   console.log('Mouse over, set Font Awesome of ID ' + data.originalTarget.id + " here");
});

element.addEventListener('mouseout', function(data) {
  console.log('Mouse out, remove Font Awesome of ID ' + data.originalTarget.id  + " here");
});
} 

var event = new MouseEvent('mouseover', {
  'view': window,
  'bubbles': true,
  'cancelable': true
});
<a class="level-item like-icon">
    <div class="icon is-medium">
        <i id="hoverIcon1" class="hover-icon far fa-heart">ICON1</i>
        <i id="hoverIcon2" class="hover-icon far fa-heart">ICON2</i>
    </div>
</a>
Hacène
  • 253
  • 1
  • 8