skip to Main Content

The intended effect of the code below is that it displays 2 red circles that turn to gray if the mouse hovers over them:

.hovered {
  fill: gray !important; /* Apply the gray fill color */
  stroke: blue !important; /* Add a blue border */
  stroke-width: 8px !important; /* Set border width */
}
<!-- JavaScript class for managing general behavior -->

<!-- SVG containing the symbol -->
<svg style="display: none;">
  <symbol id="redCircle" viewBox="0 0 200 200">
    <circle cx="50" cy="50" r="40" fill="red" />
    <script><![CDATA[   
      // Wait for the document to be fully loaded before setting up event listeners
      document.addEventListener('DOMContentLoaded', () => {
        // Add mouseover and mouseout event handlers to each <use> element
        const useElements = document.querySelectorAll('[*|href="#redCircle"]');
        useElements.forEach(useElement => {
          const color = useElement.getAttribute('data-color') || 'red';
          useElement.addEventListener('mouseover', () => {
            console.log("hover");
            useElement.classList.add('hovered');
          });

          useElement.addEventListener('mouseout', () => {
            useElement.classList.remove('hovered');
          });
        });
      });
    ]]></script>
  </symbol>
</svg>

<!-- SVG containing two <use> references to the symbol with color properties -->
<svg id="svg-container">
  <use xlink:href="#redCircle" id="use1" x="100" data-color="blue" />
  <use xlink:href="#redCircle" id="use2" data-color="green" />
</svg>

If I remove the fill="red" from the symbol it does what I want.
but I want the code to work regardless of the "fill=" attribute of the symbol.

So, here is some stuff I tried:

This CSS works, but only if I don't apply fill="red" to the symbol: 
<style>
.hovered {
  fill: gray; /* Apply the gray fill color */
  stroke: blue; /* Add a blue border */
  stroke-width: 8px; /* Set border width */
}
</style>

This CSS doesn't work at all:
<style>
.hovered circle {
  fill: gray; /* Apply the gray fill color */
  stroke: blue; /* Add a blue border */
  stroke-width: 8px; /* Set border width */
}
</style>

This CSS doesn't work at all:
<style>
/* CSS to define the hovered class */
.hovered use circle {
  fill: gray; /* Apply the gray fill color */
  stroke: blue; /* Add a blue border */
  stroke-width: 8px; /* Set border width */
}
</style>

and this CSS is the only one that can override the fill="red":
<style>
circle {
  fill: gray; /* Apply the gray fill color */
  stroke: blue; /* Add a blue border */
  stroke-width: 8px; /* Set border width */
}
</style>

The Javascript code is inside the SVG for a specific reason and I like to keep it like that.

3

Answers


  1. I have given id="circleId" to the red circle.

    I am tracking and storing it by getElementId under const redC.

    And modifying the colour by redC.style.fill= "gray" on mouseover and redC.style.fill= "red" on mouseout. Works fine.

        .hovered {
          fill: gray !important; /* Apply the gray fill color */
          stroke: blue !important; /* Add a blue border */
          stroke-width: 8px !important; /* Set border width */
        }
        <svg style="display: none;">
          <symbol id="redCircle" viewBox="0 0 200 200">
            <circle id="circleId" cx="50" cy="50" r="40" fill="red" />
            <script><![CDATA[   
              // Wait for the document to be fully loaded before setting up event listeners
              document.addEventListener('DOMContentLoaded', () => {
                // Add mouseover and mouseout event handlers to each <use> element
                const redC= document.getElementById("circleId");
                const useElements = document.querySelectorAll('[*|href="#redCircle"]');
                useElements.forEach(useElement => {
                  const color = useElement.getAttribute('data-color') || 'red';
                  useElement.addEventListener('mouseover', () => {
                    console.log("hover");
                    useElement.classList.add('hovered');
                    redC.style.fill= "gray";
                  });
    
                  useElement.addEventListener('mouseout', () => {
                    useElement.classList.remove('hovered');
                   redC.style.fill= "red"; 
                  });
                });
              });
            ]]></script>
          </symbol>
        </svg>
    
        <!-- SVG containing two <use> references to the symbol with color properties -->
        <svg id="svg-container">
          <use xlink:href="#redCircle" id="use1" x="100" data-color="blue" />
          <use xlink:href="#redCircle" id="use2" data-color="green" />
        </svg>
    Login or Signup to reply.
  2. I have modified your code a bit to accomodate my example, removed the JS and introduced :hover with CSS custom properties.

    As HTML data attributes currently can still only be used as stringresults from CSS attr(..) I think it would be best to use CSS custom properties instead.

    The base principle

    1. At parent level assign generic color variables to be used by child elements:
    • #svg-container { color: red }, set a default color (sets currentColor)
    • #svg-container { --primary: blue; --secondary: --green /* extend at will */ }, to set required colors. For page wide use, set them at :root level.
    1. Assign override colors at child level to display time color variable --dsp-color:
    • #use1 { --dsp-color: blue }, a simple, direct override.

    • #use2 { --dsp-color: var(--primary, magenta) }, default to magenta when --primary is undefined.

    • #use3 { --dsp-color: var(--secondary, yellow) }, default to yellow when --secondary is undefined.

    • extend this list as required or use classes instead.

    • Instead, the above rules can also be defined in the specific HTML <use> as a style="..." e.g. <use id="use1" style="--dsp-color: blue">, etc.

    1. Use display color variable --dsp-color to finally display a circle with the required color:
    • circle { fill: var(--dsp-color, currentColor) }, uses the display time color variable and the current parent color value. This sets the appropriate color overriding the fill set in a <circle> (which essentially has become redundant).

    The snippet is commented and should be self explanatory combined with the above. I added a few extra variables to show a more generic hover effect.

    /*
        Main color definitions
    */
    #svg-container {
        /* change to any default color you require, sets 'currentColor' */
        color: red;
    
        /* disable these to see defaults in effect */
        --primary  : blue;   /* disable to show magenta circle */
        --secondary: green;  /* disable to show yellow circle */
        /**/
    
        /* Optionally define hover effects */
        --hover    : gray;
        --stroke   : blue;
        --str-width: 8px;
    } 
    /*
        Specific Color overrides per <use> case
        When undefined, they fallback to a default color
    */
    #use1 { --dsp-color: blue }
    #use2 { --dsp-color: var(--primary  , magenta) }
    #use3 { --dsp-color: var(--secondary, yellow)  }
    
    /*
        Color handling SVG circle
    */
    #redCircle circle { /* optionally using #redCircle for more specificity */
        /* When --dsp-color is undefined it defaults to 'currentColor' defined in #svg-container */
        fill: var(--dsp-color, currentColor); /* overrides 'fill' in <circle> */
    }
    
    #redCircle circle:hover { /* optionally using #redCircle for more specificity */
    
        fill  : var(--hover, gray);  /* Apply --hover fill color, default gray */
        stroke: var(--stroke, blue); /* Apply --stroke color, default blue */
    
        stroke-width: var(--str-width, 8px); /* Apply --str-width, default 8px */
    
        cursor: pointer;
    }
    <svg style="display: none;">
        <symbol id="redCircle" viewBox="0 0 400 200">
            <circle cx="50" cy="50" r="40" fill="red" />
            <!-- 'fill' is redundant here, gets overridden -->
        </symbol>
    </svg>
    
    <!-- SVG containing four <use> references to the symbol with color properties -->
    <svg id="svg-container" style="">
        <use xlink:href="#redCircle" id="use1" x="0"   />
        <use xlink:href="#redCircle" id="use2" x="75"  />
        <use xlink:href="#redCircle" id="use3" x="150" />
        <use xlink:href="#redCircle" id="use4" x="225" />
    </svg>
    Login or Signup to reply.
  3. I want the code to work regardless of the "fill=" attribute of the symbol.

    Here is a straightforward CSS approach, in which:

    1. you apply the fill value to the <use> elements (via each element’s id)
    2. the <circle> inherits the fill value from the step above, via fill: inherit.

    Working Example:

    svg {
      width: 100%;
      height: 180px;
    }
    
    circle {
      fill: inherit;
    }
    
    circle:hover {
      fill: gray;
      stroke: blue;
      stroke-width: 8px;
    }
    
    use[href="#redCircle"] {
      fill: red;
    }
    
    #use1 {
      fill: blue;
    }
    
    #use2 {
      fill: green;
    }
    <svg>
      <defs>
        <symbol id="redCircle">
          <circle cx="50" cy="50" r="40" />
        </symbol>
      </defs>
    
      <g id="container">
        <use id="use1" href="#redCircle" x="0" />
        <use id="use2" href="#redCircle" x="100" />
        <use id="use3" href="#redCircle" x="200" />
        <use id="use4" href="#redCircle" x="300" />
      </g>  
    </svg>

    Further Reading:

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search