Apply :hover to all children of an svg group - css

How do I get all the elements of an svg group to change their fill color on hover?
The example below does not work at all. If I use .sgroup circle:hover only the circle under the pointer works not both.
.sgroup:hover {
fill: green;
}
<div>
<svg width="200" height="200">
<g class="sgroup">
<circle cx="50" cy="50" fill="pink" r="10" ></circle>
<circle cx="150" cy="150" fill="purple" r="10" ></circle>
</g>
</svg>
</div>

Please see below. I assume this is what you're looking for?
.sgroup:hover circle {
fill: green;
}
<div>
<svg width="200" height="200">
<g class="sgroup">
<circle cx="50" cy="50" fill="pink" r="10" ></circle>
<circle cx="150" cy="150" fill="purple" r="10" ></circle>
</g>
</svg>
</div>

Related

Set origin and rotation on animateMotion's path

I'd like to apply a rotation on a path animated with SVG's animateMotion tag.
It seems that the rule transform-origin:50%;transform: rotate(240deg); applied on the path that the animateMotion tag follows doesn't alter the animation.
<path id="theMotionPathIdLikeToRotate" d="m 100,100 -3e-6,-52.916668 45.82718,26.458333 0,52.916665" stroke-width="5px" stroke="aqua" fill="none" style="transform-origin:50%;transform: rotate(120deg);" stroke-linecap="round" stroke-linejoin="round" />
My aim was to create an animation and repeat it transformed. In this example I wanted to create other moving circles, rotated 120 and 240 degrees around the center of the hexagon.
Only the path definition (d) of the path referenced by an <mpath> element is used. Any transform it might have is ignored.
You would need to apply the transform to the circle and the <mpath> together.
<g style="transform-origin:50%;transform: rotate(240deg);">
<circle cx="0" cy="0" r="5" fill="#333333">
<animateMotion dur="4.45s" repeatCount="once">
<mpath xlink:href="#theMotionPath3"/>
</animateMotion>
</circle>
</g>"
<!DOCTYPE HTML>
<html>
<body>
<?xml version="1.0"?>
<svg width="400" height="400" viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink" style="background:aquamarine">
<style>
path {
animation-name:animateDash;
animation-duration:5s;
animation-iteration-count:once;
}
#keyframes animateDash {
from{stroke-dasharray:0,2305}
to {stroke-dasharray:2305,0}
}
</style>
<circle cx="50%" cy="50%" r="1" fill="firebrick" />
<path id="theMotionPath" d="m 100,100 -3e-6,-52.916668 45.82718,26.458333 0,52.916665" stroke-width="5px" stroke="antiquewhite" fill="none" stroke-linecap="round" stroke-linejoin="round" />
<path id="theMotionPath2" d="m 100,100 -3e-6,-52.916668 45.82718,26.458333 0,52.916665" stroke-width="5px" stroke="aqua" fill="none" style="transform-origin:50%;transform: rotate(120deg);" stroke-linecap="round" stroke-linejoin="round" />
<path id="theMotionPath3" d="m 100,100 -3e-6,-52.916668 45.82718,26.458333 0,52.916665" stroke-width="5px" stroke="azure" fill="none" style="transform-origin:50%;transform: rotate(240deg);" stroke-linecap="round" stroke-linejoin="round" />
<circle cx="0" cy="0" r="5" fill="#333333">
<animateMotion dur="4.45s" repeatCount="once">
<mpath xlink:href="#theMotionPath3"/>
</animateMotion>
</circle>
<g style="transform-origin:50%;transform: rotate(120deg);">
<circle cx="0" cy="0" r="5" fill="#333333">
<animateMotion dur="4.45s" repeatCount="once">
<mpath xlink:href="#theMotionPath3"/>
</animateMotion>
</circle>
</g>"
<g style="transform-origin:50%;transform: rotate(240deg);">
<circle cx="0" cy="0" r="5" fill="#333333">
<animateMotion dur="4.45s" repeatCount="once">
<mpath xlink:href="#theMotionPath3"/>
</animateMotion>
</circle>
</g>"
<!--- HIDES animateMotion's reset-->
<circle cx="" cy="" r="20" fill="aquamarine" />
<script type="text/javascript">
console.log(theMotionPath.getTotalLength());
</script>
</svg>
</body>
</html>

Portions of SVG that were offscreen while zoomed in disappear when zooming back out

I'm using css transitions to animate zooming into an SVG. The only problem is that when zooming out, portions of the SVG are missing until the animation is complete and then it all pops in.
(Only tested in Chrome on a Mac so far)
I'm not changing the SVG at all, just zooming in and then back out by setting the a scale transform on a group in the SVG.
How can I make the browser re-render these offscreen elements so that don't pop in like this?
const root = document.getElementById('root')
setTimeout(function() {
root.setAttribute('transform', 'scale(10,10)')
}, 1)
setTimeout(function() {
root.setAttribute('transform', 'scale(1,1)')
}, 4200)
#root {
transition: 4s transform;
}
circle {
stroke: white;
stroke-width: 3px;
}
<svg viewbox="0 0 300 100">
<g id="root">
<circle cx="50" cy="50" r="50" />
<circle cx="100" cy="50" r="50" />
<circle cx="150" cy="50" r="50" />
<circle cx="200" cy="50" r="50" />
<circle cx="250" cy="50" r="50" />
</g>
</svg>
As said in comments, this is probably because of some optimizations in the CSS renderer.
This is a Chrome bug (one of the many they have with their CSS paintings optimizations...), and you should let them know about it.
For the time being, have you considered using SMIL instead?
Since you used javascript in your code, I will assume you run this in a browser from some place where script execution is allowed (i/e not in <img> tag), and hence where you will be able to use a polyfill like FakeSmile.
So this will actually offer you a better browser support than through CSS transitions (IIRC IE<11 didn't support CSS transform transitions on svg elements), and moreover than the still experimental SVG2 only mix-up CSS transition of SVGTransformAttribute.
Indeed, only Chrome does support it for now (probably because while some attributes were already CSS transitionable in SVG1.1, transform having a different syntax than its CSS equivalent, the algo should be differents).
Here is what your example would look like in SMIL:
// and if you need JS control
document.onclick = e => {
document.getElementById('zoomin').beginElement();
};
circle {
stroke: white;
stroke-width: 3px;
transform: translateZ(1);
}
<svg viewbox="0 0 300 100">
<g id="root">
<animateTransform attributeName="transform" type="scale" id="zoomin"
from="1 1" to="10 10" dur="4s" begin="1s"/>
<animateTransform attributeName="transform" type="scale" id="zoomout"
from="10 10" to="1 1" dur="4s" begin="zoomin.end"/>
<circle cx="50" cy="50" r="50" />
<circle cx="100" cy="50" r="50" />
<circle cx="150" cy="50" r="50" />
<circle cx="200" cy="50" r="50" />
<circle cx="250" cy="50" r="50" />
</g>
<!-- for IE -->
<script xlink:href="https://cdn.rawgit.com/FakeSmile/FakeSmile/master/smil.user.js"></script>
</svg>
How about, transition every circle,
is this ok for you?
I actually dont have an explanation about why it works this way
const circles = document.getElementsByTagName('circle')
setTimeout(function() {
circles[0].setAttribute('transform', 'scale(10,10)');
circles[1].setAttribute('transform', 'scale(10,10)');
circles[2].setAttribute('transform', 'scale(10,10)');
circles[3].setAttribute('transform', 'scale(10,10)');
circles[4].setAttribute('transform', 'scale(10,10)');
}, 1)
setTimeout(function() {
circles[0].setAttribute('transform', 'scale(1,1)');
circles[1].setAttribute('transform', 'scale(1,1)');
circles[2].setAttribute('transform', 'scale(1,1)');
circles[3].setAttribute('transform', 'scale(1,1)');
circles[4].setAttribute('transform', 'scale(1,1)');
}, 4200)
#root {
transition: 4s transform;
}
circle {
stroke: white;
stroke-width: 3px;
transition: 4s transform;
}
<svg viewbox="0 0 300 100">
<g id="root">
<circle cx="50" cy="50" r="50" />
<circle cx="100" cy="50" r="50" />
<circle cx="150" cy="50" r="50" />
<circle cx="200" cy="50" r="50" />
<circle cx="250" cy="50" r="50" />
</g>
</svg>
Why not transition the entire svg element?
const root = document.getElementById('root')
setTimeout(function() {
root.setAttribute('transform', 'scale(10,10)')
}, 1)
setTimeout(function() {
root.setAttribute('transform', 'scale(1,1)')
}, 4200)
#root {
transition: 4s transform;
transform-origin: top left;
}
circle {
stroke: white;
stroke-width: 3px;
}
<svg id="root" viewbox="0 0 300 100">
<g>
<circle cx="50" cy="50" r="50" />
<circle cx="100" cy="50" r="50" />
<circle cx="150" cy="50" r="50" />
<circle cx="200" cy="50" r="50" />
<circle cx="250" cy="50" r="50" />
</g>
</svg>
The setAttribute need the style attribue. Try this script:
setTimeout(function() {
root.setAttribute('style', 'transform: scale(10,10)')
}, 1)
setTimeout(function() {
root.setAttribute('style', 'transform: scale(1,1)')
}, 4200)

SVG circle take all the height preserving aspectratio on any div size

The idea is to draw a circle in SVG that takes the whole height on the parent div whatever the size of the parent div. The width should be somehow ignored.
I've been working a bit with aspect ratio of SVG but this does not really work on all scenarios :
<div style='width:400px;height:100px'>
<svg width="100%" height="100%" viewbox="0 0 200 100" preserveAspectRatio="xMinYMin slice">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">Example SVG text 1</text>
</svg>
</div>
With the div width defined with 200px it's working.
Fiddle: https://jsfiddle.net/91sp2j0x/11/
Specifying a value of 50 for the r attribute will allow the nested svg element to maintain a 100% height of its containing (parent) element.
Code Snippet Demonstration:
.resize-demonstration {
resize: auto;
border: 1px solid gray;
box-sizing: border-box;
overflow:hidden;
}
.container-model {
border-right: 1px dashed gray;
}
<p>Resize the element below <u>vertically</u> or <u>horzontally</u> to demonstrate the intended behaviour</p>
<p><em>Note:</em> the <code>svg</code> has been wrapped in an containing element for <em>user-friendly resizing</em> (interaction with the resizing icon in the bottom-right corner), this is <strong>only for the sake of demonstration</strong> and should not be considered required.</p>
<div class="resize-demonstration" style="height: 100px">
<div style='width:50px;height:100%;overflow:hidden;' class="container-model">
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="xMinYMin meet">
<circle cx="50" cy="50" r="50" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">100px width : Nothing is visible</text>
</svg>
</div>
</div>
Updated JSFiddle
Is this what you would like to happen? I set the height of the div for each of the divs to different heights, and set the divs to display: inline-block so that the width is not 100%, as divs usually are by default, since they are block elements. That way the height of the SVG will be the height of the div.
<div style='height:200px;display:inine-block;overflow:hidden'>
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="xMinYMin meet">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">200px h</text>
</svg>
</div>
<div style='height:500px;display:inine-block;overflow:hidden'>
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="xMinYMin meet">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">500px h</text>
</svg>
</div>
<div style='height:300px;display:inine-block;overflow:hidden'>
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="xMinYMin meet" >
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">300px h</text>
</svg>
</div>
https://jsfiddle.net/suefeng/v14e7b81/2/
with preserveAspectRatio="none" and svg{width:100%} will give 100% adjusted height.
svg{
width: 100%;
}
<div style='width:200px;height:100px;overflow:hidden'>
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="none">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">200px w: cut text</text>
</svg>
</div>
<div style='width:500px;height:100px;overflow:hidden'>
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="none">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">500px w: full visible text</text>
</svg>
</div>
<div style='width:50px;height:100px;overflow:hidden'>
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="none" >
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">100px width : Nothing is visible</text>
</svg>
</div>
If i understood right, u want to have the circle with the radius of the height...
I did that successfully like so (if i understood what u asked correctly):
<div style='width:800px;height:120px;'>
<svg viewbox="0 -10 200 140" preserveAspectRatio="xMinYMin slice"> /* just remove the height and the width from the svg, it will take the parameters from the div... */
<circle cx="18%" cy="18%" r="18%" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">Example SVG text 1</text>
</svg>
</div>

svg grouped elements jointly hover

Is it possible for hover on an svg element cause other elements with the same class to hover too without jQuery? Or do I have to next the two into an outer group?
I have inside an inline svg the following groups:
<g class="class1">
<path....>
<path....>
</g>
<g class="class1">
<path....>
<path....>
</g>
I then have in my CSS:
class1 {
...
}
class1:hover {
...
}
I guess you can't do it directly, but you can achieve it by adding an id to the parent element, no classes needed, like that:
#circles:hover circle{
fill: Wheat;
}
<svg id="circles" width="100" height="260" >
<circle cx="50" cy="50" r="40" stroke="Tomato" stroke-width="4" fill="Tomato" />
<circle cx="50" cy="140" r="40" stroke="Tomato" stroke-width="4" fill="Aquamarine"/>
</svg>

Hovering over transparent SVGs

How do I get hover to work robustly with transparent SVG polygons?
In the code below, you'll see that the second triangle isn't readily recognizing :hover (and it fails completely when stroke attribute is deleted or none'd). In the third triangle, hover starts to work with transparency, but only near the text.
<html><head><style>
body { background-color: Green }
polygon:hover {
fill:Red;
}
g:hover polygon {
fill:Red;
}
</style></head><body>
<svg width="300px" height="600px" viewBox="0 0 100 200" xmlns="http://www.w3.org/2000/svg" version="1.1">
<polygon fill="white" stroke="black" stroke-width="0.5px" points="0,0 100,0 100,100 0,0" />
<polygon fill="none" stroke="black" stroke-width="0.5px" points="0,0 0,100 100,100 0,0" />
<g>
<polygon fill="none" stroke="black" stroke-width="0.5px" points="0,100 100,100 100,200 0,100" />
<text x="50" y="150" font-family="Verdana" font-size="30">hi</text>
</g>
</svg>
</body></html>
I've confirmed this over Chrome, Firefox, and Safari. Any ideas (that'll work across most browsers)?
For each element state the fill color as fill="red", and set 0 as the fill opacity fill-opacity="0". On hover change the fill-opacity to 1:
body {
background-color: Green
}
polygon:hover {
fill-opacity: 1;
}
g:hover polygon {
fill-opacity: 1;
}
<svg width="300px" height="600px" viewBox="0 0 100 200" xmlns="http://www.w3.org/2000/svg" version="1.1">
<polygon fill="white" stroke="black" stroke-width="0.5px" points="0,0 100,0 100,100 0,0" />
<polygon fill="red" fill-opacity="0" stroke="black" stroke-width="0.5px" points="0,0 0,100 100,100 0,0" />
<g>
<polygon fill="red" fill-opacity="0" stroke="black" stroke-width="0.5px" points="0,100 100,100 100,200 0,100" />
<text x="50" y="150" font-family="Verdana" font-size="30">hi</text>
</g>
</svg>
Another approach is to use pointer-events="all".
With pointer-events you can control wich part of your shape reacts to pointer-events, independently of its fill or stroke.
circle:nth-of-type(1) {
pointer-events: fill
}
circle:nth-of-type(2) {
pointer-events: all
}
circle:nth-of-type(3) {
pointer-events: stroke
}
circle:nth-of-type(4) {
pointer-events: none
}
circle:hover {
fill: red;
stroke: blue
}
<svg width="300px" height="300px" viewBox="0 0 100 100">
<circle cx="25" cy="25" r="20" fill="none" stroke="red" stroke-width="5" />
<circle cx="75" cy="25" r="20" fill="none" stroke="none" stroke-width="5" />
<circle cx="25" cy="75" r="20" fill="green" stroke="none" stroke-width="5" />
<circle cx="75" cy="75" r="20" fill="green" stroke="red" stroke-width="5" />
</svg>

Resources