I have an svg that I'm trying to access and modify using d3.js. The svg file name is us-map.svg. I've included a reference to the svg in my html like this:
<object id="imgMap" data="us-map.svg" type="image/svg+xml">
</object>
I can select imgMap in my chrome watch panel like this:
var imgMap = d3.select('#imgMap')
However, I'm not able to select child elements. For example, my imgMap svg has several child <g> elements but the child elements are not returned with this function:
d3.select('#imgMap').selectAll('g')
Am I missing something here? I was thinking that d3 could be used to traverse and manipulate an existing svg?
I was thinking that d3 could be used to traverse and manipulate an existing SVG
This is pretty much what d3 does best. But when you write:
d3.select('#imgMap')
You are not selecting the SVG (unless you have an SVG with id = "imgMap", which is not your case). You're using an <object>. Thus, you have to write:
var mySVG = d3.select(document.getElementById("imgMap").contentDocument);
And then select your groups using mySVG.
var myGroups = mySVG.selectAll("g");
Have in mind that this selection only works after the object has been loaded.
Source: https://benfrain.com/selecting-svg-inside-tags-with-javascript/
EDIT:
As requested by the OP, this is a basic working demo: https://plnkr.co/edit/RJOznJROiqTpo5dm9M7L?p=preview
In this plunkr, "mysvg.svg" is an external file (in your code, you'll have to provide the correct path). The code finds the SVG:
var mySVG = d3.select(document.getElementById("imgMap").contentDocument);
And then selects the blue circle inside the SVG, moving it to the right:
var myCircle = mySVG.select("#blueCircle");
myCircle.transition().duration(2000).attr("cx", 180);
Pay attention to this: I set a setTimeout of 1000ms, just to make sure that the object is loaded before the code runs.
Related
I'm working on an HTML display for some phylogenetic analysis. I create a div, and there's this lovely javascript package called jsPhyloSVG that draws me a nice tree in that div. That package uses raphael to do its drawing, but it doesn't return me the raphael paper object. I'd like to make a few additions to this image using raphael, but I don't know how because I don't have a reference to that paper object. I can access the svg that raphael generates, but is there some way to work backwards to find paper?
If that doesn't work, I could always just add circle or path objects directly to the svg using jquery, right?
Well, after most of a day smashing my head into a wall, I've solved my issue. It turns out you can reuse the paper object that has been created by some other library, if you can find a reference to the object itself. For anyone else that is using jsPhyloSVG, this can be found by:
var dataObject = {phyloxml: tree_data};
var phylocanvas = new Smits.PhyloCanvas(
dataObject,
'svgCanvas',
1000, 1000,
'circular'
);
var paper = phylocanvas.getSvg().svg;
You can then proceed as normal: paper.circle(100, 100, 15);.
What I think Ian was getting at, is that you cannot reuse the svg that has been created by raphael. I could always access that element, but using jQuery to append circle elements didn't work.
They would show up in the svg object in the inspection panel, but would not display on the screen. There are a few workarounds discussed in another question here, which mostly worked for me. However they broke the connection between the raphael object and the svg element on screen; most mouseover functionality stops working.
Assume, I have some relatively complex SVG graphics as a set of files, for example, looking like these icons:
I want to use them in my D3-powered charts. So, I can go with <defs> and <use> tags and inject them as symbols. But I want to make them less solid monolithic assets, but more like fully active and editable graphic elements. I know I can manually import all paths from icons SVG code like this:
svg.append('path').attr('d', 'M7.5,5.809c-0.869,0-1.576-0.742-1.576-1.654c0-0.912,0.707-1.653,1.576-1.653 c0.87,0,1.577,0.742,1.577,1.653C9.077,5.067,8.369,5.809,7.5,5.809z')
But this doesn't seem to be a quick to run scenario as I need to build some script to convert icons to code like that or do it manually, but I want to have some simple workflow similar to just editing an icon Illustrator, saving, importing.
As a result, I want to have full control on the all the shapes and paths inside each icon.
How you think it can be done in the most straightforward and D3 way?
You can import your SVGs using d3.xml and then insert the xml directly into your document. In the example code below I create a g in a svg element and then insert an image into that:
<script>
var height = 500;
var width = 700;
var vis = d3.select("#vis").append("svg")
.attr("width", width).attr("height", height)
var g = vis.append("g").attr("id", "image");
d3.xml("drawing.svg", "image/svg+xml", function(xml) {
g.each(function() {
this.appendChild(xml.documentElement);
});
});
</script>
I guess you could also select separate icons from one SVG document containing multiple icons and insert each separately. I expect styling will be lost when the SVG document stores it's styling in a stylesheet and not directly in the elements.
Trying to implement nested SVG elements with Raphael.
I think this Question is related to
https://groups.google.com/forum/#!topic/raphaeljs/tzdj3y2DDwg
Any solution? Thanks.
Actually, I've maneged to partially implement nested svg approach.
This is a short example:
var outer_svg = Raphael(document.getElementById('container'), paper_width, paper_height);
outer_svg.canvas.setAttribute('id', 'outer_svg');
var inner_svg = Raphael(document.getElementById("outer_svg"), paper_width, paper_height/2);
inner_svg.canvas.setAttribute('y', paper_height/2);
Here we created inner_svg that is half the height of outer_svg and is positioned at the bottom of outer_svg.
The canvas object represent the svg dom element. So if we give it an id attr then we can adress it to be the parent of another svg. And we can set the x,y coords relative to parent svg element.
Now the sad part:seems that canvas doesn't support animation method, so we cannot animate the whole svg child element. Sets don't help here because drawing elements parts that are outside child svg canvas disappear (as they should).
Hi is there a way of using javascript for example using buttons to change colour of an svg shape? If so could someone please guide me in the right direction thanks
If you have a number of these shapes, then look at the d3 library, which is designed explicitly to allow you to bind data to svg attributes. A good explanation of the way it works is the Three little circles tutorial.
If you want to just change an attribute of an svg shape on a button click, then you need an onclick handler for the button:
function handleClick() {
// code to modify svg here, e.g.:
document.getElementById('svgShapeId').setAttribute('cx',150);
}
document.getElementById('buttonId').onclick = handleClick;
Here's an example of using JS to create animation elements to highlight colors based on mouse over/out:
http://phrogz.net/SVG/change-color-on-hover.svg
Here's an example of an SVG that changes lots of colors, and house some silly mouseover buttons:
http://phrogz.net/SVG/rgbhsv.svg
Here's an example that shows SVG in XHTML, with both native HTML widgets (an HTML5 slider) as well as draggable SVG elements (the path handles):
http://phrogz.net/SVG/area_of_path.xhtml
In general:
Find elements
Attach event handlers
In the event handlers, adjust properties (either via setting XML attributes or via the SVG DOM)
Is there any accurate way to get the real size of a svg element that includes stroke, filters or other elements contributing to the element's real size from within Javascript?
I have tried pretty much everything coming to my mind and now I feel I'm coming to a dead end :-(
Updated question to add more context (Javascript)
You can't get the values directly. However, you can get the dimensions of the bounding rectangle:
var el = document.getElementById("yourElement"); // or other selector like querySelector()
var rect = el.getBoundingClientRect(); // get the bounding rectangle
console.log( rect.width );
console.log( rect.height);
It is supported at least in the actual versions of all major browser.
Check fiddle
Both raphael js http://dmitrybaranovskiy.github.io/raphael/ and d3 js http://d3js.org/ have various methods to find the size of an svg object or sets of svg object. It depends on if it's a circle, square, path, etc... as to which method to use.
I suspect you are using complex shapes, so in that case bounding box would be your best bet http://raphaeljs.com/reference.html#Element.getBBox
(Edit: updated reference site.) http://dmitrybaranovskiy.github.io/raphael/reference.html#Element.getBBox
Here is an example using D3.js:
Starting with a div:
<div style="border:1px solid lightgray;"></div>
The javascript code looks like this:
var myDiv = d3.select('div');
var mySvg = myDiv.append('svg');
var myPath = mySvg.append('path');
myPath.attr({
'fill': '#F7931E',
'd': 'M37,17v15H14V17H37z M50,0H0v50h50V0z'
});
// Get height and width.
console.log(myPath.node().getBBox());
If it is an SVG used as a CSS background image and you're using React you can use background-image-size-hook.
import { useBackgroundImageSize } from 'background-image-size-hook'
const App = () => {
const [ref, svg] = useBackgroundImageSize()
console.log(svg) // { width, height, src }
return <SVGBackgroundImageComponent ref={ref} />
}
You didn't specify any programming language. So I can suggest to use Inkscape.
In the file menu you find document's properties and in the first page there's "resize page to content" command. In this way you remove all the white space around your draw and you see the real size. After width and height values apprear inside the header of svg.
I know that Inkscape supports scripting and command line operations but I don't know if it's possible to do the trimming operatation in this way. But if it's possible you can do that from every programming language.