I have an external SVG file which contains the following SVG definition:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 150" ><path class="st0" fill="#FFFFFF" d="M50,145.5C40.6,131.6,2.5,73.4,2.5,50C2.5,23.8,23.8,2.5,50,2.5S97.5,23.8,97.5,50C97.5,73.4,59.4,131.6,50,145.5z"/><path class="st1" fill="#000000" d="m50 5c24.8 0 45 20.2 45 45 0 19.5-29.4 67.7-45 91.1-15.6-23.4-45-71.6-45-91.1 0-24.8 20.2-45 45-45m0-5c-27.6 0-50 22.4-50 50s50 100 50 100 50-72.4 50-100-22.4-50-50-50z"/><circle fill="#ffbf00" cx="50" cy="50" r="27.5"/><path d="m50 25c13.8 0 25 11.2 25 25s-11.2 25-25 25-25-11.2-25-25 11.2-25 25-25m0-5c-16.6 0-30 13.4-30 30s13.4 30 30 30 30-13.4 30-30-13.4-30-30-30z"/></svg>
The SVG has multiple paths, so for me to be able to change colours of individual paths I need to be able to load the SVG file contents into a Javascript variable like this:
var svgSource = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 150" ><path class="st0" fill="#FFFFFF" d="M50,145.5C40.6,131.6,2.5,73.4,2.5,50C2.5,23.8,23.8,2.5,50,2.5S97.5,23.8,97.5,50C97.5,73.4,59.4,131.6,50,145.5z"/><path class="st1" fill="#000000" d="m50 5c24.8 0 45 20.2 45 45 0 19.5-29.4 67.7-45 91.1-15.6-23.4-45-71.6-45-91.1 0-24.8 20.2-45 45-45m0-5c-27.6 0-50 22.4-50 50s50 100 50 100 50-72.4 50-100-22.4-50-50-50z"/><circle fill="#ffbf00" cx="50" cy="50" r="27.5"/><path d="m50 25c13.8 0 25 11.2 25 25s-11.2 25-25 25-25-11.2-25-25 11.2-25 25-25m0-5c-16.6 0-30 13.4-30 30s13.4 30 30 30 30-13.4 30-30-13.4-30-30-30z"/></svg>';
Then I can use conditional statements to alter the colours e.g.
switch(centerId) {
case 1:
svgSource = svgSource.replace("#ffbf00", "#005D00");
break;
case 2:
svgSource = svgSource.replace("#ffbf00", "#A20000");
break;
case 3:
svgSource = svgSource.replace("#ffbf00", "#ffbf00");
break;
}
I could define and use the inline SVG code hard coded into the Javascript as shown, but for maintenance and continuity it would be much better to use existing, centralised, external SVG files.
How can I load the contents of the SVG file into a javascript variable/object?
CSS Styling
Try using CSS to style the paths instead of replacing parts of source code.
Concept code example, using the style attribute of svg elements to apply some CSS:
"use strict";
const svg = document.querySelector("svg");
const paths = svg.querySelectorAll("path");
paths[0].style = "fill: #FF0000;"
svg.querySelector("circle").style = "fill: rebeccapurple";
svg {
height: 150px;
width: 100px;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 150" ><path class="st0" fill="#FFFFFF" d="M50,145.5C40.6,131.6,2.5,73.4,2.5,50C2.5,23.8,23.8,2.5,50,2.5S97.5,23.8,97.5,50C97.5,73.4,59.4,131.6,50,145.5z"/><path class="st1" fill="#000000" d="m50 5c24.8 0 45 20.2 45 45 0 19.5-29.4 67.7-45 91.1-15.6-23.4-45-71.6-45-91.1 0-24.8 20.2-45 45-45m0-5c-27.6 0-50 22.4-50 50s50 100 50 100 50-72.4 50-100-22.4-50-50-50z"/><circle fill="#ffbf00" cx="50" cy="50" r="27.5"/><path d="m50 25c13.8 0 25 11.2 25 25s-11.2 25-25 25-25-11.2-25-25 11.2-25 25-25m0-5c-16.6 0-30 13.4-30 30s13.4 30 30 30 30-13.4 30-30-13.4-30-30-30z"/></svg>
Potentially you could improve on this by writing svg source that expects to be styled by CSS instead of being over-ridden by style attribute values.
See also: How to use external SVG in HTML?
Getting the source of an external SVG file
An external svg file can be included in HTML using a pair of <object> tags. For example if "svg-file.svg" is in the same directory as the HTML file:
<object type="image/svg+xml" id="mySVG"
data="svg-file.svg"
width="100"
height="150"
></object>
Subject to security policies: the svg file must be from the same domain and served from a network or localhost server - due to blanket security restriction placed on local files, the source of svg files loaded using the file:// protocol. can't be accessed.
Picking up the svg source is a bit obscure. This worked for me after page load, using the above HTML:
window.onload = ()=> {
console.log("loaded");
const svgObject = document.querySelector("#mySVG");
const svg = svgObject.getSVGDocument().documentElement;
const svgSource = svg.outerHTML;
console.log("svgElement, tag name '%s' ", svg.tagName, svg);
console.log("svgSource: ", svgSource);
console.log("path.st0: ", svg.querySelector('.st0'));
};
One solution is to load the svg image by setting the innerHTML property of an arbitrary container to your svg string.
This then gives you two options: firstly, for heavy changes, you have access to each path as part of the DOM and can maniulate them using javascript. This simply requires making a reference to container.children[0]; Alternatively, for simple changes such as colour changes, it makes each element directly targetable by style rules where you can specify colours in the usual way.
The snippet illustrates adding the svg to the dom using your string, and resets some colours with simple style rules. Making a reference for changes using javascript is shown commented at the foot of the code.
let svgSource = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 150" ><path class="st0" fill="#FFFFFF" d="M50,145.5C40.6,131.6,2.5,73.4,2.5,50C2.5,23.8,23.8,2.5,50,2.5S97.5,23.8,97.5,50C97.5,73.4,59.4,131.6,50,145.5z"/><path class="st1" fill="#000000" d="m50 5c24.8 0 45 20.2 45 45 0 19.5-29.4 67.7-45 91.1-15.6-23.4-45-71.6-45-91.1 0-24.8 20.2-45 45-45m0-5c-27.6 0-50 22.4-50 50s50 100 50 100 50-72.4 50-100-22.4-50-50-50z"/><circle fill="#ffbf00" cx="50" cy="50" r="27.5"/><path d="m50 25c13.8 0 25 11.2 25 25s-11.2 25-25 25-25-11.2-25-25 11.2-25 25-25m0-5c-16.6 0-30 13.4-30 30s13.4 30 30 30 30-13.4 30-30-13.4-30-30-30z"/></svg>';
const container = document.getElementsByTagName('div')[0];
container.innerHTML = svgSource;
// for js manipulation:
// svgObject = container.children[0];
div {
width: 50%;
}
.st0 {
fill: red;
}
.st1 {
fill: yellow;
}
circle {
fill: green;
}
<div>
</div>
I have an link which has the svg file with the format of svg .
svgfilelink
And this link has the following content :
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20px" height="20px" viewBox="0 0 20 20" version="1.1">
<title>Rectangle Copy 14</title>
<defs>
<linearGradient x1="5.7671933%" y1="50.1613046%" x2="99.3732272%" y2="50.1613046%" id="linearGradient-1">
<stop stop-color="#59D08F" offset="0%"/>
<stop stop-color="#26C2DC" offset="100%"/>
</linearGradient>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="06-Shapes-Properties" transform="translate(-1166.000000, -354.000000)" stroke="url(#linearGradient-1)" stroke-width="2">
<rect id="Rectangle-Copy-14" x="1167" y="355" width="18" height="18"/>
</g>
</g>
As of now I'm adding this link as src in img tag .
But I need this link as svg format.
I have tried few methods. But didn't help.
Is there any possible ways to convert the url into svg tag in JAVASCRIPT?
I don't know if this is the right way to do but you can directly fetch that svg and render it to the DOM using innerHTML or something like dangerouslySetInnerHTML with react
let svgUrl = 'https://raw.githubusercontent.com/rahuldkjain/github-profile-readme-generator/master/src/images/icons/Social/hackerrank.svg'
function getTheSvg(url) {
return fetch(url).then(res => res.text());
}
getTheSvg(svgUrl).then(res => {
let svgDiv = document.querySelector('.svg')
svgDiv.innerHTML = res
})
svg {
height: 200px
}
<div class='svg'><div>
If you used create-react-app, it already uses #svgr/webpack package, so you can do this, which will cause the image to load inline as React components containing the SVG elements directly, instead of by a link:
import { ReactComponent as RectangleIcon } from './rectangle.svg';
export const MyComponent = () => {
return <RectangleIcon />
}
If you are not using create-react-app so not using svgr yet, you can add it to your project (see docs)
Is there a way of displaying images with a geoJson mask in Leaflet/another map library? I took "choropleth map of US States" example and I'd like to display a different background image in each state. How can I achieve it?
Here's my code https://jsfiddle.net/d37zg8f9/, as you can see the images overflow from each state. In a perfect world something like that can be achieved by following HTML:
<svg>
<mask id="mask">
<path d=".."></path>
</mask>
<image mask="url(#mask)" href=".."></image>
</svg>
But I'm not sure if there's a way of injecting that into Leaflet.
https://jsfiddle.net/9rzt2uoe/29
You can use the fill property in CSS to fill SVG paths with SVG patterns.
We need an SVG to hold a bunch of patterns that each contain an image:
<body>
<div id='map'></div>
<svg id='patterns' width="0" height="0">
<defs id='defs'>
</defs>
</svg>
</body>
In onEachFeature, instead of adding image overlays on the map, images are added as patterns in defs in the patterns SVG:
let i = 300;
const svgPatternsDefs = document.getElementById('defs');
function onEachFeature(feature, layer) {
const imageUrl = `https://picsum.photos/${i++}`;
const imageBounds = layer.getBounds();
const imageOverlay = L.imageOverlay(imageUrl, imageBounds);
//imageOverlay.addTo(map);
const imageId = `picture-${i}`;
defs.innerHTML += `
<pattern id='${imageId}' width="1" height="1" viewBox="0 0 100 100" preserveAspectRatio="none">
<image xlink:href='${imageUrl}' width="100" height="100" preserveAspectRatio="none"></image>
</pattern>
`;
Promise.resolve().then(() => { // defers execution
layer.getElement().style.fill = `url(#${imageId})`;
});
}
layer.getElement() returns undefined until the layers get added to the map. Code execution can be deferred with Promise.resolve().then
const geoJson = L.geoJson(statesData, {
onEachFeature,
style : {
"fillOpacity" : 1
}
}).addTo(map);
fillOpacity is 0.2 by default and should be set to 1 for images to be shown correctly.
I am currently working on a project where I need to start with an SVG template file, load it into the DOM and finally append some elements to it based on some data. I am accomplishing this primarily with d3.
Most of this process works perfectly. However, text that I add to the svg does not appear on the page despite showing up in the html upon inspection.
Other svg elements like cirlces do show up as intended.
If it helps, this svg is being loaded into the popup html of a chrome plugin.
Any help would be greatly appreciated. I will reproduce the JS code that adds the SVG element below and the html it produces. Please let me know if I should provide any additional information.
JS code:
async function buildSVG (data) {
const divsvg = d3.select("#svg-container").node(0)
//add background svg as starting template
await d3.xml("img_templates/breaking_news_square.svg")
.then(data => {
divsvg.append(data.documentElement)
});
const result = await addElements(data)
console.log(result)
}
function addElements (data) {
console.log("Adding elements")
return new Promise(resolve => {
//select svg d3 object
const svg = d3.select("svg")
console.log(svg.node().getBBox())
// console.log(svg)
//Add quote text
const quoteGroup = svg.append("g")
.attr("class", "quoteGroup")
.attr("transform", `translate(${margin.left},${margin.top})`)
const quoteText = quoteGroup.append("text")
.attr("class", "quote")
.text("Test text")
result = {result: "test"}
resolve(result)
})
}
Resulting HTML
<svg xmlns="http://www.w3.org/2000/svg" width="484" height="484" viewBox="0 0 484 484" fill="none">
<path d="M0 0H484V484H0V0Z" fill="#A81F1F"></path>
<path fill-rule="evenodd" clip-rule="evenodd" d="M392.084 263.038L438.464 66.545C440.986 55.8608 434.004 45.2547 423.193 43.3484C412.393 41.4442 402.212 49.0042 400.912 59.8927L376.972 260.361C376.489 264.411 379.253 268.133 383.269 268.842C387.281 269.549 391.148 267.003 392.084 263.038ZM424.061 38.4244C410.434 36.0217 397.588 45.5608 395.947 59.2998L372.008 259.768C371.209 266.453 375.771 272.597 382.401 273.766C389.023 274.933 395.406 270.731 396.95 264.187L443.331 67.6937C446.513 54.2124 437.702 40.8297 424.061 38.4244Z" fill="white" fill-opacity="0.5"></path>
<path fill-rule="evenodd" clip-rule="evenodd" d="M376.473 311.306C381.912 312.265 387.098 308.634 388.057 303.195C389.016 297.756 385.385 292.569 379.946 291.61C374.507 290.651 369.32 294.283 368.361 299.722C367.402 305.161 371.034 310.347 376.473 311.306ZM375.605 316.23C383.763 317.669 391.543 312.222 392.982 304.063C394.42 295.905 388.973 288.125 380.814 286.686C372.656 285.248 364.876 290.695 363.437 298.854C361.999 307.012 367.446 314.792 375.605 316.23Z" fill="white" fill-opacity="0.5"></path>
<g class="quoteGroup" transform="translate(42,100)"><text class="quote">Test text</text></g></svg>
Your text is there, but it currently inherits fill="none" from the <svg> element, making it invisible. Try this:
...
.text("Test text")
.attr("fill", "black")
I have an HTML file that embeds two different SVG files, like so:
<html>
<body>
<object id="svg0" data="histograms.svg" type="image/svg+xml"></object>
<object id="svg1" data="test.svg" type="image/svg+xml"></object>
</body>
</html>
Both SVG files are interactive, by adding a javascript function that is triggered by onclick, like such:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:ns1="http://www.w3.org/1999/xlink" height="172pt" version="1.1" viewBox="0 0 1209 172" width="1209pt">
<script type="text/ecmascript">
function choose(obj) {
var values = [0.08,0.77];
var names = [ "hist_1", "hist_2", "hist_3", "hist_4", "hist_5", "hist_6", "hist_7", "hist_8", "hist_9", "hist_10", "hist_11" ];
for ( var i=0; i<names.length; i++) {
var o = document.getElementById( names[i] );
o.style['opacity'] = values[0];
}
obj.style['opacity'] = values[1];
}
</script>
...
<g id="figure_1">
<g id="patch_1">
<path d=" M0 172.8 L1209.6 172.8 L1209.6 0 L0 0 z " style="fill:#ffffff;" />
</g>
<g id="axes_1">
<g cursor="pointer" id="hist_1" onclick="choose(this)">
<path d=" M20.835 70.52 L189.696 70.52 L189.696 12.96 L20.835 12.96 z " style="fill:#ffe6cc;" />
</g>
...
How can I have a click in one SVG file trigger javascript in the other SVG file? (Possibly via top level .html file as intermediate, if necessary?)
If you're writing code in test.svg then top gets you the containiner, so
var svg0 = top.document.getElementById("svg0");
would get you the object element from the container document.
Then
obj0Document = svg0.contentDocument;
if (obj0Document && obj0Document.defaultView)
obj0Window = obj0Document.defaultView;
else if (svg0.window)
obj0Window = svg0.window;
gets you the content's document and window.
accessing the SVG document's "window" allows you to access variables and functions defined in scripts in the SVG document.
e.g. obj0Window.choose(something)
Everything must have the same domain for this to work.