Confused about SVG styling with d3.js - javascript

The most d3.js examples use SVG to draw the diagrams, etc. This means for instance rect instead of div. This then again means, that CSS properties like drop-shadow cannot be applied.
The alternative approach is to define filters in <defs>. The same is true if you want to fill the rect with a gradient color. Is that right so far?
So I define some filter and gradients, but it seems very redundant to redefine these definitions for every page again. Wouldn't it be possible to reference all these definitions from a separate svg-file?
I tried things like: filter: url(.../my_file.svg) but it does not seem to work, but isn't that how it's supposed to be?

Say you have a file called filters.svg, and it contains a few filter defitions. It looks something like this:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<filter id="my_filter" x="0%" y="0%" width="100%" height="100%">
...
</filter>
</svg>
If you were using my_filter in that same document, you'd just refer to it using #my_filter. But you're not. You want to use it in another document. What you have to do then is refer to it in those documents as /filters.svg#my_filter. Like this:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0" y="0" width="100" height="100" fill="url(/filters.svg#my_filter)" />
</svg>
This is of course assuming that both documents are accessible from the same domain, and that they're located in the right places, etc. I don't know if this will work in CSS stylesheets, but it will work across SVG documents. Check out the section on Linking in the SVG Specification for more details.

Related

How can I change an SVG color without using an ID in the SVG file itself?

Newbie to working with SVGs and I've run into a problem. Basically I'm using the Noun Project API pro version ( https://api.thenounproject.com/ ) to scrape icons as SVGs. Right now, I save the SVGs in a local folder. I want to be able to change the color of the icon (right now they download as black, and I want to change the color). I am loading the SVGs via the tag with a reference to the folder where the SVGs are being saved. I know it's fairly simple to change the color using vanilla JS (using document.getElementById('svgObject').contentDocument, and then accessing the inner document using a unique id). The problem is that the SVGs I save don't have any ID, and I don't know how to give them an ID. Right now, I'm looping through the folder and displaying the folder contents in a webpage. That's all working fine, but I can't manage to figure out how to change the color.
Basically, I just want all the icons in the folder to be set to a different color (for example, they could all be set to "red." They don't need to each have a different color). How do I do that without specifying the actual ID (since the SVG doesn't include an ID upon download), or else how do I add an ID to the SVG tag itself?
Assuming you have a bunch of svgs on your page and you'd like to change the color of each to the same color, you could do the following:
function changeSvgColors() {
const svgs = document.getElementsByTagName('svg')
for (let i = 0; i < svgs.length; i++) {
svgs[i].setAttribute('fill', 'red')
}
}
This will loop through every svg on your page and set their fill attribute to red.
Essentially, this is equivalent to doing this:
<svg viewBox="0 0 512 512" fill="red">
...
</svg>
Here's a quick demo:
JSFiddle
Alternatively, if you're using something like the img tag to load your svgs onto your page, using object instead would let you do the same thing as above.
Assuming your html looks like the following:
<div>
<object
data="http://localhost:5000/image.svg"
type="image/svg+xml"
></object>
</div>
Then,
function changeSvgColors() {
const objects = document.getElementsByTagName('object')
for (let i = 0; i < objects.length; i++) {
const object = objects[i]
const svg = object.contentDocument.rootElement
svg.setAttribute('fill', 'red')
}
}
One option would be using the svg as an image (or as an object if you prefer) and filters to change the color.
#theImage{filter: invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%);}
<img id="theImage" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/bone300.svg" >
Please read this article: Solved with CSS! Colorizing SVG Backgrounds
Yet another option is using the svg as a mask. Keep in mind that the support for mask is not that good.
#theDiv{
display: inline-block;
width:300px;
height:134px;
-webkit-mask: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/bone300.svg);
mask: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/bone300.svg);
mask-size: cover;
background:red;
}
<div id="theDiv"></div>
But the best option would be to use the svg inline. You may save all your icons in a root svg element with width="0"; height="0"; position="absolute". Next you can use the icons with <use> and you change the color using the fill attribute <use fill="red" xlink:href="..... or styling it in CSS
You can give the SVGs a class instead of an id & set the color using javascript's getElementsByClassName
This Mozilla article explains:
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
Your best shot in achiveing this is to make use of the fill attibute for svg elements. Now, you can work this out two ways
Method 1:
Using img tag like this <img src="your_file.svg" />. In this case, you will need to edit the file directly, and that's it, it should reflect everywhere you reference the svg.
Method 2:
You can use svg as inline like below. The advantage in this approach is that if you have varying instance of the same svg, then this is the way to go. On the other side, if your svg stays same throughout the app, this goes against the DRY principle.
svg {
max-width: 200px;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<path d="M25,7l28,36l40-18l-36,25l19,44l-27-37l-41,17l36-26z" fill="#cfc"/>
<ellipse cx="50" cy="50" rx="32" ry="30" stroke="#fc0" fill="none" stroke-width="11"/>
<path d="M63,2l-7,43l42,17l-45-6l-16,42l7,-45l-42-16l45,6z" fill="#3cc"/>
</svg>
If you don't mind having the svg all one color, you could set the fill attribute of the svg to currentcolor. Then it will take on the color of the element that it is in.
#span1 {
color: red;
}
#span2 {
color: blue;
}
#span3 {
color: green;
}
<body>
<span id=span1>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="150" height="150" fill-rule="evenodd" fill="currentcolor">
<title>HTML5 Logo</title>
<path d="M12,0 188,0 172,180 100,200 28,180Z M45,37 156,37 154,59 69,59 71,81 152,81 146,149 100,162 54,149 52,114 74,114 76,132 100,139 124,132 127,103 51,103Z"/>
</svg>
</span>
<span id=span2>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="150" height="150" fill-rule="evenodd" fill="currentcolor">
<title>HTML5 Logo</title>
<path d="M12,0 188,0 172,180 100,200 28,180Z M45,37 156,37 154,59 69,59 71,81 152,81 146,149 100,162 54,149 52,114 74,114 76,132 100,139 124,132 127,103 51,103Z"/>
</svg>
</span>
<span id=span3>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="150" height="150" fill-rule="evenodd" fill="currentcolor">
<title>HTML5 Logo</title>
<path d="M12,0 188,0 172,180 100,200 28,180Z M45,37 156,37 154,59 69,59 71,81 152,81 146,149 100,162 54,149 52,114 74,114 76,132 100,139 124,132 127,103 51,103Z"/>
</svg>
</span>
</body>

How do I render svg images in React Native?

I've tried "react-native-remote-svg" and "react-native-svg-image"; neither of them manage to render the SVG file.
How do I handle SVG in React-native?
Example code:
import SVGImage from 'react-native-svg-image'
const EmailLogo = require('../static/others/Email.svg');
// Render etc....
<ButtonContainer>
<Button bgColor={colors.darkTeal} txtColor={colors.whiteText}
onPress={this.onSignInPress.bind(this)}>
LOG IN WITH
</Button>
<SVGImage
style={{ width: 80, height: 80 }}
source={EmailLogo}
/>
</ButtonContainer>
Result: It's a white square when it should be an email logo.
How does one handle SVG's properly in React-native?
I've been through this struggle. react-native-svg-icon helped me out, but there were some additional things that i had to do, to make it work.
First of all, this library uses react-native-svg underneath. And you need to convert your svg files into SVG objects that this library understands.
If your open your svg file with editor, it will look something like this
<svg xmlns="http://www.w3.org/2000/svg" viewBox="170.5 200.5 18.6 23">
<defs>
<style>.a{fill:#444;}.b{fill:#07b57a;}</style>
</defs>
<g transform="translate(171 201)">
<path class="a" d="M18.1,22.5H-.5V-.5H18.1ZM.5,21.5H17.1V.5H.5Z"/>
<rect class="b" width="5.4" height="1" transform="translate(9 5.4)"/>
<path class="b" d="M4.4,7.3,3,5.9l.7-.7.7.7L6.6,3.7l.7.7Z"/>
<rect class="b" width="5.4" height="1" transform="translate(9 10.5)"/>
<path class="b" d="M4.4,12.4,3,11l.7-.7.7.7L6.6,8.8l.7.7Z"/>
<rect class="b" width="5.4" height="1" transform="translate(9 15.6)"/>
<rect class="b" width="2.5" height="1" transform="translate(3.2 15.6)"/>
</g>
</svg>
You need to convert it to something like this
entry: {
svg: (
<G transform="translate(171 201)">
<Path fill="#444444" d="M-152.4-178H-171v-23h18.6V-178z M-170-179h16.6v-21H-170V-179z" />
<Rect x="-161.5" y="-195.1" fill="#07B57A" width="5.4" height="1" />
<Path
fill="#07B57A"
d="M-166.1-193.2l-1.4-1.4l0.7-0.7l0.7,0.7l2.2-2.2l0.7,0.7L-166.1-193.2z"
/>
<Rect x="-161.5" y="-190" fill="#07B57A" width="5.4" height="1" />
<Path
fill="#07B57A"
d="M-166.1-188.1l-1.4-1.4l0.7-0.7l0.7,0.7l2.2-2.2l0.7,0.7L-166.1-188.1z"
/>
<Rect x="-161.5" y="-184.9" fill="#07B57A" width="5.4" height="1" />
<Rect x="-167.3" y="-184.9" fill="#07B57A" width="2.5" height="1" />
</G>
),
viewBox: '0 0 18.6 23',
}
This is a representation of the svg file in components of react-native-svg library. One thing you need to pay attention here, is viewbox of the svg file. I am not sure why, but most of the time, it is 'off center'. I will show in screenshots below. Because of that, it cannot be displayed by the react-native-svg-icon as well. To bring it to center you can use Adobe Illustrator, or some other online tool to edit svg. One i used is http://editor.method.ac/. So, I uploaded my svg, recentered it and downloaded it again. and used that svg file to create object in my react native code.
This is my initial svg file that i uploaded to the service. if you zoom out and press cmd+a (or ctrl+a) to select all, it will highlight svg icon, like in screenshot below. You should position it to the white part, either by dragging it, or by setting X and Y on top right corner to 0s.
This is how it will look when centered
Once you save that svg file, use it to convert it to javascript object with react-native-svg components, more info on that can be found here
Once you create you svg objects, you can use it with react-native-svg-icon. You will find how to do that in the link I shared above.
I know, this is a lot of pain and seemingly over complicated, and I spent quite some time to make it work, but it is the only way I managed to accomplish it.
One other option would be to convert your svgs into font icons with icomoon.com and use it with react-native-vector-icons. but it will only work if your svgs are drawn with only one color, as multicolored ones cannot be converted to fonts
P.S. I didn't try, but maybe, libraries that you tried to use might work with centered svg file that we got from online service. Let me know if it works, then it can be helpful to other users as well.
react-native-svg-image and react-native-svg-image uses WebView to render SVG files so it do not support local files at the moment. Its written it the docs.
Use react-native-svg-uri to render SVG images in React Native from an URL or a static file. to use react-native-svg-uri you will need to link react-native-svg as well. So read docs carefully.

Can JavaScript function outside of the [CDATA[ tag?

From what I read, JavaScript within an SVG must be put into a [CDATA[ tag (the type designation is apparently now optional):
<script type="application/javascript">
<![CDATA[
(JavaScipt stuff here)
]]>
</script>
However, the following markup/code works without that tag even though it looks like JavaScript methods are being used:
<svg width="400px" height="400px" viewBox="0 0 400 400">
<rect id="rect1" x="160" y="10" width="60" height="60" fill="blue"
onmouseenter="evt.target.setAttribute('fill', 'red');"
onmouseout="evt.target.setAttribute('x', '0');"
/>
</svg>
So, are the evt.target.setattribute commands all SVG? I know this is a super newbie question...but I am having a hard time establishing where SVG ends and JavaScript begins. Documentation is very sketchy on the delineation, and I want to learn the total extent of what I can do in SVG before needing to learn another language. Thanks much for any clarification. And if anyone knows of a reference that is very clear on the subject, I'd love to know.
A CDATA section is a feature of XML documents. It's not part of html. You seem to be using SVG in html given that your SVG example has not namespaces so CDATA is neither required nor useful there.
In standalone SVG you need namespaces defined i.e. xmlns="http://www.w3.org/2000/svg" and there you need CDATA to prevent various characters in javascript e.g. < being interpreted as the beginning of a tag. Other characters are problematic too.

How to change part of an inline SVG's appearance when hovering over another outside source div or text element using javascript?

I have been looking for an HTML/CSS only solution to this issue for about 2 weeks now and nothing has come from any research or experimentation. I have come to the understanding that this is not possible with CSS so I was wondering if anyone could provided me a Javascript example that could possibly work for my case as javascript isn't my strong suit at the moment. My issue is that I have a multi-component SVG element. It is inline in the HTML and within a section tag. There is also a set of text elements in the section above the SVG. What I am trying to make happen is that when a portion of the SVG is hovered, the corresponding text element and the hovered SVG piece both change color. Vice versa if the text element is hovered, the corresponding SVG piece will change color as well.
<section>
<h1>Section 1</h1><h1>Section 2</h1>
<svg id="World_Map"...<path class="_russia_"...</svg>
</section>
It is probably important to note that the SVG has multiple paths with unique ID's and these are the pieces that I am trying to get working in this fashion.
Yes, without the SVG elements being siblings or ancestors to your text elements this is very difficult (if not impossible) to do with CSS. I have seen some pretty tricky and amazing things done with CSS selectors, though, so I wouldn't rule it out that someone more clever than me could get it to work. :)
Anyway, I have a working example of what you asked for using JavaScript. It actually makes use of jQuery because there is the .hover() binding, which cleanly allows you to bind a single function to the mouseenter and mouseleave events simultaneously, and the .toggleClass() method, which will add a class to an element if it doesn't exist or remove the class from the element if it's already there.
Since you did not post any functional code, here is a JSFiddle with my simple example.
https://jsfiddle.net/davecripps/o6mwjokj/
$(function() {
$("h1").hover(function() {
$("#map" + $(this).attr("id").substring(4)).toggleClass("mapHover");
});
$("rect").hover(function() {
$("#text" + $(this).attr("id").substring(3)).toggleClass("textHover");
});
});
.textHover,
h1:hover {
color: red;
cursor: pointer;
}
.mapHover,
rect:hover {
fill: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<section>
<h1 id="textRussia">Russia (yellow)</h1>
<h1 id="textSpain">Spain (blue)</h1>
<h1 id="textFrance">France (green)</h1>
<svg width="340" height="100" viewbox="0 0 340 100">
<rect id="mapRussia" x="0" y="0" width="100" height="100" fill="yellow" />
<rect id="mapSpain" x="120" y="0" width="100" height="100" fill="blue" />
<rect id="mapFrance" x="240" y="0" width="100" height="100" fill="green" />
</svg>
</section>
You should be able to understand the methodology and apply to your code.

Changing path fill in svg file using css and javascript

Ok, i spent all day to trying "connect" css styles with svg files created in inkscape. I've got svg file with map and couple of path, and i want to change fill color when i mouseover on that path. Please, can You help me with that ? :/. If i'am creating
<img src="example.svg" />
, than in css style i put for example:
<style type="text/css">
#path6666
{ fill: green;
}
</style>
nothing happen. I dont wanna use jquery, only javascript and css.
Please, help !..
UPDATE:
This is svg code:
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg2985"
version="1.1"
inkscape:version="0.48.4 r9939"
width="1176"
height="617"
sodipodi:docname="mainMapa.svg">
<metadata
id="metadata2991">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2989">
<inkscape:path-effect
effect="spiro"
id="path-effect3794"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect3790"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect3786"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect3782"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect3778"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect2997"
is_visible="true" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1280"
inkscape:window-height="962"
id="namedview2987"
showgrid="false"
inkscape:zoom="0.97789116"
inkscape:cx="374.5729"
inkscape:cy="311.15508"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg2985" />
<image
width="1176"
height="617"
xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/
2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a
HBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy
BRRRQAUUUUAFFFFAB
RRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFF
FFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUU
UAFFFFABRRRQAUUUUAf/2Q==
"
id="image2993"
x="0"
y="0" />
<path
style="fill:#2b54e6;stroke:#281300;stroke-width:1px;stroke-linecap:butt;
stroke-linej oin:miter;stroke-opacity:1 ;fill -opacity:1"
d="m 308.82783,502.72348 3.57913,4.60174 5.36869,2.04521 5.11305,
3.57914 4.34608,3.83478 -0.76695,3.32348 -4.34609,4.09043 -3.57913,
1.27826 -4.09044,
-1.78956 -3.83478,-2.04522 -3.57913,-0.51131 -1.78956,-4.09043 -0.51131,
-5.62435 1.53391, -6.3913 z"
id="path3833"
inkscape:connector-curvature="0" />
</svg>
I'am, starting giving up. I dont understand that: inkscape is very popular free software. Why there is no ANY tutorial how do that ?!
Looks like you have three problems here:
Style cascade issue
Inkscape is defining the fill of path #path3833 in an inline style declaration. That's more specific than your style sheet rule, so it overrides it.
MDN has some good information about how the cascade works.
To solve your problem, in the style-sheet use !important on the declaration to override Inkscape's inline syle:
#path3833 {
fill:green !important; /* override inline style */
}
Hosting the SVG in HTML
Note that Robert Longson's answer, to use <object type="image/svg+xml" data="[your file]"> rather than <img> is accurate; you can't change any part of an SVG hosted in an img tag.
Linking the CSS to the SVG
Static linking
To link the stylesheet you can add the following XML declaration to the SVG document:
<?xml-stylesheet type="text/css" href="your_style_sheet.css"?>
This should sit between the opening XML declaration <?xml version="1.0" ..., but come before the root svg element. Typically this means it should be the second line of the file.
Dynamic linking
To add a stylesheet through Javascript, you must create an XML processing instruction (i.e. the <? ... ?> markup). The SVG document object exposes a method to do that. However, getting a reference to the SVG document is tricky; you have to go through window.frames. That means you must know which "frame" of the parent HTML document contains the SVG. If you have one SVG file, it's easy. Otherwise, you might have to use trial-and-error.
In any case, this is roughly how it should go:
var frameNumber = 0; //this may vary depending on your page
var svgDoc = window.frames[frameNumber].document;
var procInstruction = svgDoc.createProcessingInstruction(
'xml-stylesheet', // the type of processing instruction
'type="text/css" href="your_style_sheet.css"' // the "data" of the PI
);
svgDoc.insertBefore( // the PI must come *before* the SVG root element
procInstruction, svgDoc.childNodes[0]
);
This fiddle demonstrates the two techniques above, with data URIs instead of files. I can't get the dynamic part to work with data URIs, but if you can use a file instead it will work fine. (Firefox v26 / Win7)
When you use SVG as an image i.e. via an image tag or background-image css it isn't interactive i.e. mouseover and the like don't work.
This is so the raster mental model people have for images is consistent with SVG.
If you want interactivity you'll have to include the <svg> via and <object> or <iframe> tag instead.
I found a solution a long time ago, but I am writing it because someone might use it in the future. The solution was removing style from the path. Then everything worked perfectly.
Also I have to say: Inkscape is the best program for SVG that I have ever tried.

Categories

Resources