Changing style of SVG elements in a g group using JS - javascript

Can I change the css style, such as fill or stroke, for all elements within a g group using JS?
Here's the svg, script and the external css:
<g id="trajectories" class="trajectory">
<circle id="tr1" cx="0" cy="0" r="75" />
<circle id="tr2" cx="0" cy="0" r="110" />
<circle id="tr3" cx="0" cy="0" r="160" />
<circle id="tr4" cx="0" cy="0" r="230" />
<circle id="tr5" cx="0" cy="0" r="350" />
<circle id="tr6" cx="0" cy="0" r="475" />
<circle id="tr7" cx="0" cy="0" r="625" />
<circle id="tr8" cx="0" cy="0" r="750" />
</g>
function mouseDown() {
svgElement = document.getElementById("trajectories");
svgElement.style.stroke = "Black";
}
g.trajectory{
fill: none;
stroke: rgba(30,30,30,1);
}
The circles in the g inherit the fill and stroke of the class g.trajectory when first drawn, but attempting to change the styling of the g element does nothing. I could change the styles of the circles individually, but is it possible to change them all just by targeting the g element?

You tried targetting the circle with straight css?
I.E
g circle {color:#ff0000;}
On the fiddles below i added a little bit in between your circle tags just for demo purpose
Here is a fiddle of this http://jsfiddle.net/vTNrR/
Or with jquery
$('#trajectories circle').css('color','#ff0000');
Here is a fiddle of the JQuery approach http://jsfiddle.net/vTNrR/4/
Or with javascript
var svgElement = document.getElementById("trajectories"); // get the parent node
var circles = svgElement.getElementsByTagName('circle'); // get child nodes
You now have an array of elements in circles so can do something like
for (var i=0;i<circles.length;i++)
{
circles[i].style.color="#ff0000";
}
Here is a fiddle of the pure JS version http://jsfiddle.net/vTNrR/1/

What you are doing should work. It works for me. See here:
http://jsfiddle.net/cSk3f/
However you are changing from dark grey to black, so the change is not very noticeable. So in my demo I have used green (rgb(30,80,30)) instead.

Related

Restrict CSS in .SVG file to that SVG only

We use Umbraco for our customers, and we want to allow them to change icons for certain content.
It's possible to upload and select a custom SVG.
Because we work on a template base, we want to control the color of the icons.
Using javascript, we transform the <img> with an SVG to a HTML <svg>-tag.
That way, we can alter the fill of stroke in the SVG's using regular CSS.
But we ran into an issue, where a customer had uploaded multiple SVG icons which were all having internal CSS and using the same class-names for paths, circles and what not.
The problem is, the last SVG that's on the page overrules the CSS for all SVG's.
For example:
<svg ...>
<defs>
<style>.a{fill: red;}</style>
</defs>
<circle class="a" cx="50" cy="50" r="50" />
</svg>
<svg ...>
<defs>
<style>.a{fill: blue;}</style>
</defs>
<circle class="a" cx="50" cy="50" r="50" />
</svg>
The customer expects a red and a blue circle, because that's what he selected within the CMS.
But this renders to 2 blue circles.
Is there any way to keep CSS found in an SVG within that same SVG?
With a hint from #Lain, I have changed the to script.
It now add's a random-generated classname to the SVG, and prepends that classname to every class in the tag like this:
let styleTag = $svg.find('style');
if (styleTag.length) {
let svgClass = GenerateClassname(8);
const regex = /\.([a-z]{1})/ig; // Get every . followed by a letter
styleTag[0].innerHTML = styleTag[0].innerHTML.replace(regex, "." + svgClass +" .$1");
}
When you inject the svg into the html document, you can add a unique id to each svg and then use it in the css selector.
<svg id="svg-1">
<defs>
<style>#svg-1 .a{fill: red;}</style>
</defs>
<circle class="a" cx="50" cy="50" r="50" />
</svg>
<svg id="svg-2">
<defs>
<style>#svg-2 .a{fill: blue;}</style>
</defs>
<circle class="a" cx="50" cy="50" r="50" />
</svg>
And then, in your near future, you do want to style those individual elements with (global) styling.
shadowDOM (optional part of the native Web Components spec aka Custom Elements)
was created for use-cases like these
supported in all modern browsers
"outside/global" CSS does not leak in
"inside" CSS does not leak out
and inheritable styles, CSS properties and CSS :part can style shadowDOM
customElements.define("svg-circle", class extends HTMLElement{
connectedCallback(){
this.style.display = "inline-block";
this.style.width = "15vw";
this.attachShadow({mode:"open"})
.innerHTML = `<svg viewBox="0 0 100 100">
<style>.a { fill:${this.getAttribute("color")} }</style>
<circle class="a" cx="50" cy="50" r="45" />
<circle part="inner" class="a" cx="50" cy="50" r="20" />
</svg>`;
}
});
<style>
*::part(inner){ fill:rebeccapurple } /* higher Specificity */
</style>
<h2>Who is afraid of</h2>
<svg-circle color="red" ></svg-circle>
<svg-circle color="yellow" ></svg-circle>
<svg-circle color="blue" ></svg-circle>

SVG graph and Javascript: Which is the best way to add animation to and SVG file?

I'm new at coding.
I'm studying the way to make an animated portfolio like Sean Halpin or Stephanie Walter one. I want to put a face, in which, blinking eyes and a moving the mouth would be the animated elements. Basically, I want to play with the visibility of the closed eyes and open mouth. As an example, I drew a face as follows:
<svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 429 429">
<defs>
<style>
.cls-1 {
fill: #fff;
}
.cls-2 {
fill: none;
stroke: #000;
stroke-miterlimit: 10;
}
</style>
</defs>
<g id="face">
<path class="cls-1" d="M611,608.5a214,214,0,1,1,151.32-62.68A212.6,212.6,0,0,1,611,608.5Z" transform="translate(-396.46 -180)" />
<path d="M611,181a212.9,212.9,0,1,1-83.1,16.78A212.11,212.11,0,0,1,611,181m0-1c-118.46,0-214.5,96-214.5,214.5S492.5,609,611,609s214.5-96,214.5-214.5S729.43,180,611,180Z" transform="translate(-396.46 -180)" />
</g>
<g id="eyes">
<g id="eye_r">
<circle class="cls-1" cx="319.15" cy="128.63" r="48.5" />
<path d="M715.61,260.62a48,48,0,1,1-48,48,48.06,48.06,0,0,1,48-48m0-1a49,49,0,1,0,49,49,49,49,0,0,0-49-49Z" transform="translate(-396.46 -180)" />
</g>
<g id="iris_r">
<circle cx="319.15" cy="128.63" r="19" />
</g>
<g id="eye_l">
<circle class="cls-1" cx="109.85" cy="128.63" r="48.5" />
<path d="M506.32,260.62a48,48,0,1,1-48,48,48.06,48.06,0,0,1,48-48m0-1a49,49,0,1,0,49,49,49,49,0,0,0-49-49Z" transform="translate(-396.46 -180)" />
</g>
<g id="iris_l">
<circle cx="109.85" cy="128.63" r="19" />
</g>
<line id="closed_eye_l" class="cls-2" x1="62.04" y1="128.5" x2="158.04" y2="128.5" />
<line id="closed_eye_r" class="cls-2" x1="270.69" y1="128.23" x2="366.69" y2="128.23" />
</g>
<g id="closed_mouth">
<ellipse cx="214.5" cy="309" rx="108.5" ry="11.5" />
<path d="M611,478c29.08,0,56.41,1.25,77,3.51,30.68,3.38,31,7.32,31,7.49s-.35,4.11-31,7.49C667.37,498.75,640,500,611,500s-56.41-1.25-77-3.51c-30.69-3.38-31-7.32-31-7.49s.35-4.11,31-7.49c20.55-2.26,47.88-3.51,77-3.51m0-1c-60.2,0-109,5.37-109,12s48.8,12,109,12,109-5.37,109-12-48.8-12-109-12Z" transform="translate(-396.46 -180)" />
</g>
</svg>
So, I thought three ways to do this:
Place the svg inside an tag, calling then a function that takes into consideration the loading of the file. An example of what I'm saying is found in the following resource: https://www.petercollingridge.co.uk/tutorials/svg/interactive/javascript/, in the "External SVG + External JavaScript" part. It didn't work. The contentDocument ALWAYS returns "null".
In my example, it would be:
HTML:
<object id="face" data="path/to/face.svg" type="image/svg+xml"></object>
JS:
<script type="text/javascript">
window.addEventListener("load", function() {
var svgObject = document.getElementById('face').contentDocument;
var svg = svgObject.getElementById('closed_eye_r');
svg.style.visibility="hidden";
});
</script>
Inline SVG - call a "transform" property. Sean Halpin does it but I'm not sure what he does.
HTML: https://www.seanhalpin.design/
JS: https://www.seanhalpin.design/js/scripts.js
Inline SVG, use getElementById and apply functions to animate the internal parts of the SVG file.
Questions:
a. Is Inline SVG a good practice?
b. Which is the best way to animate an SVG?
I hope to have been clear. Let me know if something is not well presented, I want to learn to edit questions in order to make them as clear as possible.
Thanks!

How do I style one of the parameters in a circle inside an SVG?

I have a SVG with circles inside it. And I want them to be increasing and decreasing in radius for ever (like a pulsating circle).
My problem is, can I do it with #keyframes? Or do I need jquery? And if so, how?
Here is my code:
<div class="mapa">
<svg (svg code here......)
<circle opacity="0.3" cx="842" cy="451.814" r="25.582" id="1"/>
<circle opacity="0.3" cx="542" cy="405.814" r="25.582" id="1"/>
</svg>
</div>
How do I style the 'r' parameter?
I read I cannot style the 'r' parameter, but this worked:
<circle cx="168" cy="179" r="59"
fill="white" stroke="black"
onmouseover="evt.target.setAttribute('r', '72');"
onmouseout="evt.target.setAttribute('r', '59');"
/>
However, I want to do it with continuous increase and decrease in radius? And not on mouseover/mouseleave. Something like (r=25, then r=30, then back to 25, and goes on forever). How do I do this?
Thanks for your time, if you can give me any tips I'd apreciate it a lot!
Try to use svg smil animate
<svg width="150" height="150">
<circle opacity="0.3" cx="84%" cy="45%" r="3" id="1">
<animate attributeName="r" values="3; 10; 3" keyTimes="0; 0.5; 1" dur="1s" repeatCount="indefinite" />
</circle>
<circle opacity="0.3" cx="50%" cy="50%" r="10" id="2">
<animate attributeName="r" values="10; 3; 10" keyTimes="0; 0.5; 1" dur="1s" repeatCount="indefinite"/>
</circle>
</svg>
The easiest solution in CSS with a little hack around SVG containers. You change the container, not the svg. The circle element just fills 100% of the container. And the container artificially makes a circle with a border-radius.
svg {
border-radius: 50%;
transition: all 1s;
}
svg:hover {
width: 200px;
height: 200px;
}
<svg width="100" height="100">
<circle cx="50" cy="50" r="100%"
fill="green" />
</svg>
You can figure out how to implement your own keyframes, if this solution works for you.
And just to be clear, JQuery is a framework. You shouldn't bring up JQuery unless this question is about JQuery's framework. The language you're looking for is "Javascript" and it's in all major browsers by default. You can use Javascript to do this.
const grow = function(radius) {
var circle = document.getElementsByTagName("circle")[0];
circle.setAttribute('r', radius);
}
setTimeout(function() {
grow(100);
setTimeout(function() {
grow(40);
}, 2000);
}, 2000);
circle {
transition: all 1s;
}
<svg width="200" height="200">
<circle cx="100" cy="100" r="40"
fill="green" />
</svg>

Is it possible to run two different animations on one SVG element?

I'm using Snap.svg to do this animation. Basically, I want to rotate an element around a point but I want the element to still face the same way when it's rotating. Please check this http://jsfiddle.net/mabotofu/MpEu4/6/
<svg id="test">
<circle transform="translate(200,200)" r="10" fill="transparent" stroke="#aaa" />
<g>
<circle cx="0" cy="50" r="10" />
<circle cx="0" cy="150" r="10" />
<text x="0" y="100" fill="red">TEST</text>
</g>
</svg>
var paper = Snap(test),
container = paper.select("g");
container
.attr({
fill: "transparent",
stroke: "#aaa",
transform: "translate(200,200)"
})
.animate({
transform: "t200,200r720,0,0"
}, 6000);
In this example, I need the "TEST" text to still stay horizontally even when it's rotating.
toransform="rotate()" is already applied to the element so I can't use rotate again on the lement but I don't know how I can rotate around the center point then also rotate the text itself so it stays horizontally. Please help! Thank you
You can apply the rotation in the opposite direction to the text element:
...
container = paper.select("g");
text = container.select("text");
...
text.animate({transform: "t0,0r-720"}, 6000);
See: JSFiddle

Dynamic Animation for SVG Elements is not working in IE

I'm trying to do SVG element's animation while dynamically adding DOM elements with jquery. If I add those elements inside <body> as below its working.Working Sample for this is
http://jsfiddle.net/bZdfH/2/
<svg>
<script type="text/ecmascript" xlink:href="http://leunen.me/fakesmile/smil.user.js"/>
<circle cx="60" cy="60" r="20" style="fill: pink; stroke: red;" >
<animate attributeName="r" dur="4s" values="20; 0; 20" repeatCount="indefinite"/>
</circle>
</svg>
When I add it dynamically, animation will not start in IE, however it works with Chrome and FireFox.Here is what I have.
<svg>
<script type="text/ecmascript" xlink:href="http://leunen.me/fakesmile/smil.user.js"/>
<circle cx="60" cy="60" r="20" style="fill: pink; stroke: red;" onmouseover="changeImage(this)" >
</circle>
</svg>
<script>
function changeImage(circle) {
while (circle.firstChild) {
circle.removeChild(circle.firstChild);
}
circle.setAttributeNS(null, "fill", "blue");
var animate = document.createElementNS("http://www.w3.org/2000/svg", "animate");
animate.setAttributeNS(null, "attributeName", "r");
animate.setAttributeNS(null, "values", "20;0;20");
animate.setAttributeNS(null, "dur", "6s");
animate.setAttributeNS(null, "repeatCount", "indefinite");
circle.appendChild(animate);
}
</script>
Here is jsfiddle for the Working Sample.Can anyone please help me??
IE doesn't support SMIL animation.
Source : http://caniuse.com/#search=svg

Categories

Resources