Trigger CSS animation when hovering over an SVG utilized as a border-image

My web element is enclosed within an Svg border:

.element-inside-svg-border {

  border-image-source: url('images/border.svg');
  [....]
}

The border.svg file contains a CSS animation (defined in the <style> tag), like this:

<svg class="svg-frame-1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 337 198">
 
   <style>
     .svg-frame-1:hover > path {
       animation-play-state: running;
     }

     path {
       stroke:#BEA757;
       fill-opacity:0;
       stroke-width:1;
       stroke-dasharray: 1948;
       stroke-dashoffset:1948;
       animation-name: dash1;
       animation-duration: 2s;
       animation-fill-mode: forwards;
       animation-delay: 2s;  
       animation-play-state: paused; 
      }

   @keyframes dash1 {
      0%  { stroke-dashoffset:1948;}
     100%{stroke-dashoffset:0;}
    } 

  </style>

  [...]

</svg>
  

I want to trigger the animation only when hovering over the .element-inside-svg-border element.

Due to the fact that it is not inline, the <svg> does not recognize elements from the main DOM and vice versa.

Is there a solution to this issue, possibly involving JavaScript?

EDITED: based on @Danny '365CSI' Engelman's suggestion in the comments, I attempted a solution (see snippet below):

  1. Insert the SVG onto the page with animations set to "paused"

  2. In JavaScript, assign a mouseenter event where the SVG animations are switched to "running", the Svg is converted into a data URI, and then applied to the CSS border-image property as a URL value

  3. Likewise, in Js, create a mouseleave event where the SVG animations return to "paused", the Svg is transformed into a data URI once again, and assigned to the CSS border-image property

The issue arises where upon mouseenter, the animation restarts but visually resets to 0 on mouseleave. Subsequent mouseenter reveals that the animation was indeed completed during this time gap.

Why does this occur?

EDITED 2: The previous snippet had 2 flaws:

  1. Change the "animation-name" to "none" on mouseleave to pause it
  2. Prior to re-injecting the Svg as data uri in border-src, adjust the animation-duration for remaining play time. Additionally, update the @keyframes by injecting new "start values" using CSSom. Despite implementing these changes, the updated CSSom is not recognized by the Svg as data uri and thus causes the animation to restart upon subsequent mouseenter...

Answer №1

Develop a unique Custom Element called <bordered-svg> that generates the SVG dynamically with distinct values, eliminating the necessity for shadowDOM.

If shadowDOM is incorporated, the CSS becomes more straightforward, but managing it from the main DOM becomes more intricate.

<style>
  div { width: 200px;display: grid;grid-template-columns: 1fr 1fr }
  svg { width: 100%;vertical-align:top;cursor:pointer }
</style>
<div>
  <bordered-svg color="red"></bordered-svg>
  <bordered-svg color="gold"></bordered-svg>
  <bordered-svg color="green"></bordered-svg>
  <bordered-svg color="rebeccapurple"></bordered-svg>
</div>
<script>
  customElements.define("bordered-svg", class extends HTMLElement {
    connectedCallback() {
      let uniqueID = "bordered" + Math.random().toString().substr(2, 8);
      this.innerHTML = `
        <svg id="${uniqueID}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6">
        <style>
        #${uniqueID} path {
          stroke:${this.getAttribute("color")||"grey"};
          stroke-width:3; stroke-dasharray: 12;
          animation-name: animation${uniqueID};
          animation-duration: 5s;
          animation-play-state: paused; 
        }
        @keyframes animation${uniqueID} {
          0%  { stroke-dashoffset:0 }
          100%{ stroke-dashoffset:1000 }
        } 
        </style>
        <path fill='none' d='M0 0h6v6h-6z'></path></svg>`;
      let style = this.querySelector(`#${uniqueID} path`).style;
      this.onmouseenter = () => style.animationPlayState = "running";
      this.onmouseleave = () => style.animationPlayState = "paused";
    }
  });
</script>

Answer №2

Here's a different approach: using animated SVG border with play/pause functionality

<style>
  animated-border {
    --animated-border-stroke: black;
    --animated-border-fill: darkgoldenrod;
    --animated-border-playstate: running;
    --animated-border-height: 100px;
  }

  animated-border [slot="content"] {
    text-align: center;
    height: 100%;
    padding-top: 2em;
  }

</style>

<animated-border>
  <div slot="content">
    <h2>
      Once Upon A Time...
    </h2>
    <b>there was a Web Component</b>
  </div>
</animated-border>




<script>
  customElements.define("animated-border", class extends HTMLElement {
    // Animate a border picture frame. paths 1-3 are animated, path 4 is the always visible path'
    constructor() {
      let animate = (nr, duration, delay, dash = 2000) => `
      #path${nr}{
        stroke-dasharray:${dash};
        stroke-dashoffset:${dash};
        animation-name:dash${nr};
        animation-duration:${duration}s;
        animation-delay:${delay}s
      }
      @keyframes dash${nr}{
          0%{stroke-dashoffset:${dash}}
         80%{fill-opacity:0}
        100%{fill-opacity:1;stroke-dashoffset:0}}`;
      //console.warn(animate(1, "2s,1s", "0s,0s"));
      let style = `<style>:host{
        display:inline-block;
        --height:var(--animated-border-height,150px);
        height: var(--height);
        width:calc(2 * var(--height));
      }
      svg{
        background:var(--animated-border-background,transparent);
        width:100%;
        height:100%;
      }
      [id*="path"]{
        stroke:var(--animated-border-stroke);
        fill:var(--animated-border-fill);
        fill-opacity:0;stroke-width:2;
        animation-fill-mode:forwards;
        animation-play-state:var(--animated-border-playstate,paused)}
      ${animate(1,1,0)}
      ${animate(2,2,0)}
      ${animate(3,2,1)}
      </style>`;
      super().attachShadow({
        mode: "open"
      }).innerHTML = style + `<div></div>`;
      this.border = this.shadowRoot.querySelector('div');
    }
    // Render method for generating SVG paths and content
    render() {
      let random = Math.floor(1000 + Math.random() * 9000);
      let path = (nr, d) => `<path id='path${nr}' random='${random}' d='${d}'/>`;
      let svg =
        path(1, 'm23 177l293 0l0-154l-293 0l0 154zm309 4l1 0l0 13l-13 0l0-13l12 0zm-313 10l0 3l-13 0l0-13l13 0l0 10zm-9-172l-4 0l0-13l13 0l0 13l-9 0zm310-7l0-6l13 0l0 13l-13 0l0-7zm11 10l6 0l0-21l-22 0l0 17l-293 0l0-17l-21 0l0 21l17 0l0 154l-17 0l0 22l21 0l0-18l293 0l0 18l22 0l0-22l-18 0l0-154l12 0l0 0z') +
        path(2,...

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Generating table rows dynamically using functions

I am working on a table where I need to dynamically add or remove rows. Each row contains a hyperlink in the last column to delete the record. Sometimes, if the record is not found in the database, this can cause issues as new rows are added dynamically af ...

Adding -moz & -o prefixes to code that only has -webkit tags can be done by manually duplicating each line of

Imagine having a webpage located at this link: After saving the page and running the HTML file on my localhost, everything seems to work smoothly in Chrome due to the presence of -webkit prefixes in the CSS. Now the question arises - how can I ensure that ...

The page width seems incredibly wide right now, and I'm having trouble pinpointing which CSS element is responsible for this

My website is on Wordpress and the page widths seem to be too wide. I have tried looking for the culprit in the code, but so far no luck... ...

Encountering a Selenium error related to opening the shadowRoot in JavaScript

I'm currently in the process of setting up automated tests for a web application I've been developing, and I've encountered an issue that has left me puzzled. Since it's a browser-based app, I decided to start off with a basic static s ...

Scroll within the inner container

I am currently working on creating panels, each with a header and content. My goal is to allow scrolling only within the content area. Here is the CSS I have been using: .content-div{ float:left; height: 400px; overflow-x:hidden; overflow-y:hidden; ...

Is submitting data through ajax giving you trouble?

As a beginner in PHP with no knowledge of Javascript, I have been relying on tutorials to complete my JS tasks. Despite trying various solutions from StackOverflow, I have yet to achieve success! My goal is to update database values by clicking the ' ...

The set method of useState is not causing the specific component to render

My concern arises when trying to update the TaskDetail component located at Right Side. Despite changing the value of the card within the handleCardClick callback, the specific component does not reflect this change. In the provided gif, it can be observed ...

Transforming the iOS link background color as you scroll

My website features a collection of products displayed within clickable a links styled with display:block for full clickability. However, I've encountered an issue where the background color of the a link changes to an active state color when scrollin ...

Changing the shading of an arc in d3.js: Tips and tricks

I've been attempting to create a gauge with a needle that changes the background color of the ring for the past few days. My goal is to dynamically update the colors of an arc within a gauge I developed in d3.js This is my current gauge setup with t ...

Creating a basic image carousel with JavaScript, CSS, and HTML

After running the code, I encountered an issue where the first image displays but then after one second, only a white blank screen appears with no further action. It seems like there may be an error in the JavaScript code. Below is the code that was attemp ...

Make sure to trigger a callback function once the radio button or checkbox is selected using jQuery, JavaScript, or Angular

I'm looking to receive a callback once the click event has finished in the original function. <input type="radio" onchange="changefun()" /> function changefun() { // some code will be done here } on another page $(document).on('input: ...

Learn the steps for incorporating a fresh item into a table from a pop-up interface with angularjs

I have been working on a simple customers table that includes information such as name, lastname, and age. I have successfully created a function that allows users to add a new customer, which is working fine. Additionally, I have implemented a pop-up wind ...

parent triggers resolution when sending a signal to child

My routes using ui-router are structured like this: $stateProvider .state('studies', { url: '/studies?all&field&term&page&sort&sort_dir', templateUrl: 'scripts/studies/studie ...

What is the best way to insert images into a div in Ionic framework?

I am facing an issue with fitting an image inside a div container. Here is my code structure: <div style="background-color: red; height: 200px; width: 200px;"> <ion-img src="{{kategori[i].img}}"></ion-img> & ...

Using JavaScript with React to invoke an asynchronous action within a component

Within my class, I have implemented some asynchronous actions in the ComponentDidMount method. The code snippet looks like this: componentDidMount(){ // var my_call = new APICall() Promise.resolve(new APICall()).then(console.log(FB)) } class API ...

enable jQuery timer to persist even after page refresh

code: <div class="readTiming"> <time>00:00:00</time><br/> </div> <input type="hidden" name="readTime" id="readTime"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script&g ...

Internet Explorer 11 is not interested in giving attention to a disabled element

I'm trying to limit text input to only one of two fields. My approach involves checking the value of a field when it loses focus, and then disabling the other field if the initial one is not empty. Below is an example: HTML: <div class="contain ...

Discovering text on a webpage and extracting its XPath or CSS using Selenium and Java

Imagine a scenario where there is a page displaying various items on a table that changes in both order and quantity randomly. The task at hand is to locate a specific item labeled "Itemx" by its text, extract its XPath or CSS, and then use it in another ...

A Firefox Browser View of a Next.js and Tailwind Website magnified for closer inspection

After creating a website using Next.js and Tailwind CSS, I noticed that the site is appearing zoomed in when viewed in Firefox. However, it looks fine in all other browsers. When I adjust the screen to 80%, the website displays correctly in Firefox. What ...

Is it possible to unbind an event within the same function that was passed to bind in AngularJS

Recently, I encountered a challenge while attempting to remove an event in my AngularJS directive from within a function that acts as the event handler for angular.element($window).on(). Through this experience, I learned that when unbinding from the curr ...