I'm messing around with SVG and I was hoping I could create SVG files in Illustrator and access elements with Javascript.
Here's the SVG file Illustrator kicks out (It also seems to add a load of junk to the beginning of the file that I've removed)
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="276.843px" height="233.242px" viewBox="0 0 276.843 233.242" enable-background="new 0 0 276.843 233.242"
xml:space="preserve">
<path id="delta" fill="#231F20" d="M34.074,86.094L0,185.354l44.444,38.519l80.741-0.74l29.63-25.186l-26.667-37.037
c0,0-34.815-5.926-37.778-6.667s-13.333-28.889-13.333-28.889l7.407-18.519l31.111-2.963l5.926-21.481l-12.593-38.519l-43.704-5.185
L34.074,86.094z"/>
<path id="cargo" fill="#DFB800" d="M68.148,32.761l43.704,4.445l14.815,42.963l-7.407,26.667l-33.333,2.963l-4.444,14.074
l54.074-1.481l22.222,36.296l25.926-3.704l25.926-54.074c0,0-19.259-47.408-21.481-47.408s-31.852-0.741-31.852-0.741
l-19.259-39.259L92.593,8.316L68.148,32.761z"/>
<polygon id="beta" fill="#35FF1F" points="86.722,128.316 134.593,124.613 158.296,163.872 190.889,155.724 214.593,100.909
194.593,52.02 227.186,49.057 246.444,92.02 238.297,140.909 216.074,172.761 197.556,188.316 179.778,169.798 164.963,174.983
163.481,197.946 156.815,197.946 134.593,159.428 94.593,151.279 "/>
<path class="monkey" id="alpha" fill="#FD00FF" d="M96.315,4.354l42.963,5.185l18.519,42.222l71.852-8.148l20.74,46.667l-5.926,52.593
l-24.444,34.074l-25.185,15.555l-14.074-19.259l-8.889,2.964l-1.481,22.222l-14.074,2.963l-25.186,22.963l-74.074,4.444
l101.481,4.444c0,0,96.297-17.777,109.63-71.852S282.24,53.983,250.389,20.65S96.315,4.354,96.315,4.354z"/>
</svg>
As you can probably see, each element has an ID, and I was hoping to be able to access individual elements with Javascript so I could change the Fill attribute and respond to events such as click.
The HTML is bog basic
<!DOCTYPE html>
<html>
<head>
<title>SVG Illustrator Test</title>
</head>
<body>
<object data="alpha.svg" type="image/svg+xml" id="alphasvg" width="100%" height="100%"></object>
</body>
</html>
I guess this is two questions really.
Is it possible to do it this way, as opposed to using something like Raphael or jQuery SVG.
If it is possible, what's the technique?
UPDATE
At the moment, I've resorted to using Illustrator to create the SVG file, and I'm using Raphaël JS to create paths and simply copying the point data from the SVG file and pasting it into path() function. Creating complex paths such as might be needed for a map, by coding the point data manually is (to my knowledge) prohibitively complex.
Is it possible to do it this way, as opposed to using something like Raphael or jQuery SVG?
Definitely.
If it is possible, what's the technique?
This annotated code snippet works:
<!DOCTYPE html>
<html>
<head>
<title>SVG Illustrator Test</title>
</head>
<body>
<object data="alpha.svg" type="image/svg+xml"
id="alphasvg" width="100%" height="100%"></object>
<script>
var a = document.getElementById("alphasvg");
// It's important to add an load event listener to the object,
// as it will load the svg doc asynchronously
a.addEventListener("load",function(){
// get the inner DOM of alpha.svg
var svgDoc = a.contentDocument;
// get the inner element by id
var delta = svgDoc.getElementById("delta");
// add behaviour
delta.addEventListener("mousedown",function(){
alert('hello world!')
}, false);
}, false);
</script>
</body>
</html>
Note that a limitation of this technique is that it is restricted by the same-origin policy, so alpha.svg must be hosted on the same domain as the .html file, otherwise the inner DOM of the object will be inaccessible.
Important thing to run this HTML, you need host HTML file to web server like IIS, Tomcat
In case you use jQuery you need to wait for $(window).load, because the embedded SVG document might not be yet loaded at $(document).ready
$(window).load(function () {
//alert("Document loaded, including graphics and embedded documents (like SVG)");
var a = document.getElementById("alphasvg");
//get the inner DOM of alpha.svg
var svgDoc = a.contentDocument;
//get the inner element by id
var delta = svgDoc.getElementById("delta");
delta.addEventListener("mousedown", function(){ alert('hello world!')}, false);
});
If you are using an <img> tag for the SVG, then you cannot manipulate its contents (as far as I know).
As the accepted answer shows, using <object> is an option.
I needed this recently and used gulp-inject during my gulp build to inject the contents of an SVG file directly into the HTML document as an <svg> element, which is then very easy to work with using CSS selectors and querySelector/getElementBy*.
Related
I am trying to implement the hammer.js with svg files and without using any jquery. I'm trying to use the sample code from the hammer.js site {https://github.com/EightMedia/hammer.js/wiki/Getting-Started}. I have the external javascripts called like this.
<script src="javascript/hammer.js" type="text/javascript"></script>
My javascript is as follows:
<script type="text/javascript">
//<![CDATA[
var element = document.getElementById("testsvg");
var hammertime = Hammer(element).on('doubletap', function(event){
alert("doubletap!");
return false;
}
);
//]]>
</script>
My svg looks like the following.
<svg version="1.1" id="testsvg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="585px" height="230" viewBox="0 0 585 230" enable-background="new 0 0 585 230" xml:space="preserve">
<rect x="10" y="10" height="210" width="565" style="stroke:#006600; fill: #00cc00"/>
</svg>
Starting out, I'm just trying to get any detection to work. I do not get any alert when I double tap. I have also tried to implement this with the hammer.fakemultitouch.js plugin on my desktop. Neither the desktop or touch environment do anything.
Anyone have any idea what I might be doing wrongly?
Thanks,
--christopher
First make sure hammer.js is loading correctly. If it is, then you are most likely executing the javascript before the DOM is loaded. The easiest way to fix this is to place your <script> right before the closing </body> tag (near the bottom of your html file). You can also wrap your code in a load event function such as...
window.onload = function(){
var element = document.getElementById("testsvg");
var hammertime = Hammer(element).on('doubletap', function(event){
alert("doubletap!");
return false;
});
}
PROBLEM:
I'm using Snap.svg to create some basic interactive graphics, but for some reason I can't get my external SVG file to load using Snap.load(). I've pulled code straight from the tutorial at snap.io and checked and double-checked the docs. My SVG file renders in the browser fine, it just doesn't display inside the Snap SVG. Other shapes (i.e. not pulled in using Snap.load() ) do display.
CODE:
I've boiled my example down to the most simple HTML and SVG files imaginable, and the Snap.load() method still isn't working for me. Does anyone see what I'm missing?
HTML:
<head>
<style media="screen">
#svg {
width: 300px;
height: 300px;
}
</style>
<script src="snap.svg-min.js"></script>
<meta charset=utf-8 />
</head>
<body>
<svg id="svg"></svg>
<script type="text/javascript">
var s = Snap("#svg");
Snap.load("svgtest.svg");
</script>
</body>
SVG (originally exported from Illustrator):
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<rect x="14" y="33" fill="#2BB673" width="70" height="30"/>
</svg>
UPDATE:
Updated the code as per #Ian's suggestion -
var s = Snap("#svg");
Snap.load("http://www.w3.org/TR/SVG/images/struct/Use01.svg", onSVGLoaded ) ;
function onSVGLoaded( data ){
s.append( data );
}
- but still no display of external SVG. I tried using an SVG from w3.org just to be sure it wan't a problem with the file itself or my domain.
The load function takes a callback, as loading can take some time. So I think you would do something like the following...
var s = Snap("#svg");
Snap.load("svgtest.svg", onSVGLoaded ) ;
function onSVGLoaded( data ){
s.append( data );
}
Edit: There may be some access control issues if not accessing from the same server as the script, check the console log for any errors.
I was having exactly this problem in Internet Explorer only, and it turned out to be because the SVG file I was loading in was a minified one from which the doctype had been removed. Other browsers were ok without the doctype, but leaving the doctype in fixed the problem in IE as well:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
In case, like me, you're using Grunt to minify SVGs, you can leave the doctype in by adding the following option to your Gruntfile:
svgmin: {
options: {
plugins: [
{ removeDoctype: false }
]
}
// etc...
}
There is a small bug in the distribution - see https://github.com/adobe-webplatform/Snap.svg/issues/196. The suggested fix works correctly. The online demo works because it is referencing a much older build of the library.
With version 0.5.1 installed. The code in the correct answer above needs to be re-written as follows to work:
var s = Snap();
Snap.load("svgtest.svg", onSVGLoaded ) ;
function onSVGLoaded( data ){
s.append( data );
}
I have this problem that I am stuck for several days now. I am trying to append a svg to the DOM. I have custom namespaces in it and when they are added into the DOM and you try to get the innerHTML property of the parent element to which you appended them you get them with some random namespaces. This happens only in IE9.
Example:
$(document).ready(function () {
var svg = '<svg xmlns="http://www.w3.org/2000/svg" ns:attr="val" />';
alert($("div").append(svg).html());
});
The output will be:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:NS1="" NS1:a:b="val" />
Any idea how could this be solved? I've tried to define the namespaces but it's not working again. Here's a jsfiddle link http://jsfiddle.net/RwNqk/3/
Thanks in advance.
First, you have an HTML document—instead of XHTML—in your JSFiddle. HTML does not have custom namespaces, only XML/XHTML has that.
Secondly, you are using a ns namespace prefix without ever defining what that namespace is. It's a wonder that other browsers work at all.
Thirdly, even if you fix these problems, you (unfortunately) can't use jQuery to jam elements into the DOM using previously-defined namespace prefixes:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:foo="hello"><head>
<title>Using jQuery to add namespaced attribute</title>
</head><body>
<div><p foo:bar="yes">one</p></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript"><![CDATA[
var xhtml = '<p foo:bar="no">two</p>';
alert($('div').html());
try{ $('div').append(xhtml); }
catch(e){ alert(e); }
]]></script>
</body></html>
The first alert shows that the custom namespace works:
<p xmlns="http://www.w3.org/1999/xhtml" foo:bar="yes" xmlns:foo="hello">one</p>
The second alert shows the failure:
[Firefox] "An invalid or illegal string was specified" code: "12"
[Chrome] Error: SYNTAX_ERR: DOM Exception 12
[IE9] DOM EXception: SYNTAX_ERR (12)
This has nothing to do with IE9 or SVG. It mostly has to do with jQuery. (You can set the .innerHTML of the DOM element in IE9 and FF and it will work as desired, but not with Chrome.)
I wrote an HTML5 document which just contains an SVG element.It works well.
Let me open the document and cut the SVG element into the clipboard.
Now the document shows nothing in the web browser.
Let me add a textArea and a button to the document so that I can paste the removed text of SVG in runtime.
Now please let me know how to program a click handler function of the added button to dynamically restore the text of SVG element ( pasted in the added textArea ) back into the document.
Thanks in advance
The code of the original HTML5 document follows.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SVG Viewer</title>
</head><body>
<svg>
<path
d=" M 0,0 L 100,100"
fill="none"
stroke="rgb(0,102,153) " stroke-width="1" />
</svg>
</body> </html>
Perhaps this will help.
Dynamically create SVG with Javascript
http://xyfer.blogspot.com/2007/12/dynamically-create-svg-with-javascript.html
SVG standard allows to use and refer external SVG files.
I have a file circle.svg that defines a circle object with id "the_circle".
From the main SVG file I am able to include this circle and animate it, using SVG linking.
I would also like to access the same circle object via javascript, how can I do this ?
What is the javascript equivalent of xlink:href="url(#the_image)#the_circle" ?
Using document.getElementById('the_image') I can only access the SVGImageElement but not the objects defined inside the included SVG.
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
<image
id="the_image"
x="0" y="0" width="100%" height="100%"
xlink:href="circle.svg" />
<animateTransform
xlink:href="url(#the_image)#the_circle"
attributeName="transform" attributeType="XML"
type="translate"
from="0" to="25"
dur="1s" repeatCount="indefinite"
additive="replace" fill="freeze" />
</svg>
It seems like the "right" way to do this would actually be to use an SVG "use" element, rather than an image. The reason for this is that the DOM interface of the SVG use element specifies a property "instanceRoot", which allows you to get the root of the "instance tree" corresponding to that use element: http://www.w3.org/TR/SVG/struct.html#InterfaceSVGUseElement
So, you would end up with a solution that looks something like the following:
circle.svg:
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4in" height="4in" id="the_svg"
viewBox="0 0 4 4" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<circle r="1" fill="blue" stroke="none" id="the_circle"/>
</svg>
Document which uses the svg root node of circle.svg:
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" id="foo"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="circle.svg#the_svg"/>
</svg>
Unfortunately, though, while Firefox supports use of the use element with external documents, there's currently a bug in Webkit which does not allow this: https://bugs.webkit.org/show_bug.cgi?id=12499
Also, Firefox does not seem to implement the instanceRoot property for use elements.
So, it seems you may need to work around the limitations of current SVG implementations. The way I would recommend doing this is to use XMLHttpRequest to download the document to which you would like to link, and import the DOM of the downloaded document into your host document's DOM. The following code implements this, and works in Firefox, Opera and Chromium:
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" id="foo"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<script>
function fetchXML (url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function (evt) {
//Do not explicitly handle errors, those should be
//visible via console output in the browser.
if (xhr.readyState === 4) {
callback(xhr.responseXML);
}
};
xhr.send(null);
};
//fetch the document
fetchXML("http://localhost:8082/tmp/circle.svg",function(newSVGDoc){
//import it into the current DOM
var n = document.importNode(newSVGDoc.documentElement,true);
document.documentElement.appendChild(n);
var circle = document.getElementById("the_circle"); //now you have the circle
})
</script>
</svg>
You can access the necessary element a bit easier:
document.getElementById('the_image').contentDocument.getElementById('the_circle')
See this image for reference (taken on dev.opera.com)
To supplement #echo-flow's excellent solution with the code in jQuery/Coffeescript:
$.get '/assets/hexagon.svg', (svgFileData)->
svgTag = svgFileData.documentElement
$('body').append(svgTag)
circle = $('#the_circle')
Here's a solution to this problem when using React and ES6. Usage:
<SvgImage url='pathToImage.svg'></SvgImage>
https://gist.github.com/mikkel/8b79a713ff06bbec379d