skip to Main Content

I am trying to trigger a CSS animation on mouseover event. Sadly, it does not work as desired.

This is what I tried. I am trying to achieve the same motion on mouseover as the keyframe declaration within CSS would.

It works as desired in Firefox +Chrome(mobile) but not in Chrome (PC).

const element = document.querySelector('.circ');
element.addEventListener('mouseover', function() {
console.log(1);
    element.style.animation = "move 1000ms infinite ease-in-out alternate";
});
element.addEventListener('mouseout', function() {
//mouseout triggers automatically following mouseover without provocation
console.log(2); 
    element.style.animation = "none";
});
.circ {
     pointer-events: all;
}
 .rect {
     pointer-events: none;
}
 @keyframes move {
     0% {
         transform: translateX(0);
    }
     100% {
         transform: translateX(500px);
    }
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
  <div id="container" class="svg-container">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 100">
      <rect x="0" y="0" width="530" height="200" fill="#EFEFEF"></rect>
      <g class="crcl">  
        <circle class="circ" cx="10" cy="50" r="10" fill="blue"></circle>
      </g>
    </svg>
  </div>

</body>

</html>

2

Answers


  1. Chosen as BEST ANSWER

    It is possible to achieve the desired effect with existing mouseover and mouseout by overlaying the exact same transparent element as the recipient keyframe element. Then add a listener on the overlaid element to program the movement on the bottom element.

    Such as

    const data = [
        [0, 0], //bottom for action i=0, top for overlaying i=1
        [1, 1],
        [2, 2]
    ];
    
    const svg = d3.select('svg');
    
    const grp =
        svg
        .append('g')
        .classed('all', true);
    
    const circ = grp
        .selectAll('g')
        .data(data)
        .join('g')
        .attr('id', (_, i) => i)
        .attr('transform', (_, i) => `translate(0 ${20*(i+1)})`)
        .selectAll('circle')
        .data(function(_, i) {
            return data[i]
        })
        .join('circle')
        .attr('id', function(_, i) {
            return d3.select(this).node().parentNode.id + i
        })
        .attr('cx', (_, i) => 20)
        .attr('cy', (_, i) => 20)
        .attr('r', '5')
        .attr('fill', (_, i) => { if (i === 0) { return 'blue' } else { return 'transparent' } }); //bottom blue, overlay transparent
    
    
    
    d3.selectAll('svg g.all g circle:nth-of-type(2)').on('mouseover', function(_, i) {
            d3.select(
                    d3.select(this).node().parentElement.firstChild
                ).style('animation', 'move 1000ms infinite ease-in-out alternate')
                
        })
        .on('mouseout', function(_, i) {
            d3.select(
                d3.select(this).node().parentElement.firstChild
            ).style('animation', 'none')
        })
    #container>svg>g>g>circle:nth-child(1) {
        pointer-events: none;
    }
    
    @keyframes move {
        0% {
            transform: translateX(0);
        }
        100% {
            transform: translateX(500px);
        }
    }
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
        <link rel="stylesheet" href="style.css">
        <title>Document</title>
    </head>
    
    <body>
        <div id="container" class="svg-container">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 100">
            <rect x="0" y="0" width="530" height="200" fill="#EFEFEF"></rect>
          </svg>
        </div>
    </body>
    <script src="prod.js"></script>
    
    </html>


  2. The animation actually works but it constantly resets itself.

    The problem: your attaching the event listener to the animated circle.
    As this circle will start to move on mouseover it will almost immediately trigger the mouseout event.

    You should probably start and stop the animation when hovering the svg or rect instead.

    const svg = document.querySelector('svg');
    const element = document.querySelector('.circ');
    svg.addEventListener('mouseover', function() {
        element.style.animation = "move 1000ms infinite ease-in-out alternate";
    });
    svg.addEventListener('mouseout', function() {
    //mouseout triggers automatically following mouseover without provocation
        element.style.animation = "none";
    });
    .circ {
         pointer-events: all;
    }
     .rect {
         pointer-events: none;
    }
     @keyframes move {
         0% {
             transform: translateX(0);
        }
         100% {
             transform: translateX(500px);
        }
    }
      <div id="container" class="svg-container">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 100">
          <rect x="0" y="0" width="530" height="200" fill="#EFEFEF"></rect>
          <g class="crcl">  
            <circle class="circ" cx="10" cy="50" r="10" fill="blue"></circle>
          </g>
        </svg>
      </div>

    Example2: start animation on hover; stop on click

    const svg = document.querySelector("svg");
    const circles = document.querySelectorAll(".circ");
    
    circles.forEach((circ) => {
      circ.addEventListener("mouseover", (e) => {
        let current = e.currentTarget;
        current.classList.add("running");
        //current.style.animation = "move 1000ms infinite ease-in-out alternate";
      });
    });
    
    
    svg.addEventListener("click", function() {
      togglePlayState(circles);
    });
    
    function togglePlayState(els) {
      els.forEach((el) => {
        let style = window.getComputedStyle(el);
        if (style.animationPlayState === "running") {
          //el.style.animationPlayState = "paused";
          el.classList.add("paused");
          el.classList.remove("running");
    
        } else if (el.classList.contains('paused')) {
          //el.style.animationPlayState = "running";
          el.classList.remove("paused");
          el.classList.add("running");
        }
      });
    }
    .circ {
      pointer-events: all;
    }
    
    .rect {}
    
    .ani {
      animation: move 1000ms infinite ease-in-out alternate;
      animation-play-state: paused;
    }
    
    .running {
      animation-play-state: running;
    }
    
    .paused {
      animation-play-state: paused;
    }
    
    @keyframes move {
      0% {
        transform: translateX(0);
      }
      100% {
        transform: translateX(500px);
      }
    }
    <div id="container" class="svg-container">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 100">
          <rect x="0" y="0" width="530" height="200" fill="#EFEFEF"></rect>
          <g class="crcl">  
            <circle class="circ ani" cx="10" cy="50" r="10" fill="blue"></circle>
            <circle class="circ ani" cx="10" cy="75" r="10" fill="green"></circle>
          </g>
        </svg>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search