I'm trying to access inline SVG elements in JavaScript, to add a click event. I'm running into issues adding the event to anything else than a single element identified with id.
Using document.getElementById("p1_1_1") will work, but I'll need to manipulate 200 rectangles, so I will need to use document.getElementsByTagName or document.getElementsByClassName - both of which return meaningful HTMLCollections, but the addEvent won't add the event.
I've tried Firefox 17 and Chrome 23, and some additional methods, such as the getElementsByTagNameNS, bu no luck so far.
Thankful for any help you can offer.
Here are stripped down versions of the JS code and HTML/SVG I'm working with:
JavaScript:
function testing() {
var testDiv = document.getElementById("tester");
testDiv.innerHTML = '<h1>Success!</h1><br>';
}
function addEvent(obj, evt, func) {
if (obj.addEventListener) {
obj.addEventListener(evt, func, false)
} else if (obj.attachEvent) {
obj.attachEvent("on" + evt, func);
} else {
alert("no success adding event");
}
}
function init() {
var targetSquares = document.getElementsByTagName("rect");
addEvent(targetSquares, "click", testing);
}
window.addEvent(window, "load", init);
inline SVG
<!DOCTYPE html>
<html lang="en-GB">
<head>
<meta charset="utf-8">
<script type="text/javascript" src="bsroyale.js"></script>
</head>
<body>
<div id="tester">
</div>
<div id="gamemap">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 600 600">
<rect x="50" y="50" width="20" height="20" class="p1" id="p1_1_1" />
<rect x="71" y="50" width="20" height="20" class="p1" id="p1_2_1" />
<rect x="92" y="50" width="20" height="20" class="p1" id="p1_3_1" />
<rect x="113" y="50" width="20" height="20" class="p1" id="p1_4_1" />
<rect x="134" y="50" width="20" height="20" class="p1" id="p1_5_1" />
<!-- etc etc, 200 rect in total-->
</svg>
</div>
</body>
</html>
document.getElementsByTagName("rect") returns an HTML collection. You must then loop through this collection (i.e. for each...) and add the event listeners to each collection-item.
I didn't test it, but your init function could look like this:
function init() {
var targetSquares = document.getElementsByTagName("rect");
for (var i = 0; i < targetSquares.length; i++) {
addEvent(targetSquares[i], "click", testing);
}
}
Related
I have a problem with Snap.SVG and animation of multiple SVG elements.
I want to change path on hover, but i have many same SVG in html.
HTML:
var svg = $('.svg-wave');
var s = Snap(svg);
var simpleCup = Snap.select('.svg-wave-normal');
var fancyCup = Snap.select('.svg-wave-hover');
var simpleCupPoints = simpleCup.node.getAttribute('d');
var fancyCupPoints = fancyCup.node.getAttribute('d');
svg.mouseenter(function() {
simpleCup.animate({
d: fancyCupPoints
}, 600);
}).mouseleave(function() {
simpleCup.animate({
d: simpleCupPoints
}, 600);
});
svg .svg-wave-hover {opacity: 0;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script>
<div class="item">
<svg class="svg-wave" width="240" height="120" viewBox="0 0 240 120" xmlns="http://www.w3.org/2000/svg">
<path class="svg-wave-normal" d="M108.5,114.8C71.7,114.8,62.7,117,0,117h217C151,117,146.2,114.8,108.5,114.8" fill="#69c6d3"></path>
<path class="svg-wave-hover" d="M108.5,0C71.7,0,62.7,117,0,117h217C151,117,146.2,0,108.5,0" fill="#69c6d3"></path>
</svg>
</div>
<div class="item">
<svg class="svg-wave" width="240" height="120" viewBox="0 0 240 120" xmlns="http://www.w3.org/2000/svg">
<path class="svg-wave-normal" d="M108.5,114.8C71.7,114.8,62.7,117,0,117h217C151,117,146.2,114.8,108.5,114.8" fill="#69c6d3"></path>
<path class="svg-wave-hover" d="M108.5,0C71.7,0,62.7,117,0,117h217C151,117,146.2,0,108.5,0" fill="#69c6d3"></path>
</svg>
</div>
<div class="item">
<svg class="svg-wave" width="240" height="120" viewBox="0 0 240 120" xmlns="http://www.w3.org/2000/svg">
<path class="svg-wave-normal" d="M108.5,114.8C71.7,114.8,62.7,117,0,117h217C151,117,146.2,114.8,108.5,114.8" fill="#69c6d3"></path>
<path class="svg-wave-hover" d="M108.5,0C71.7,0,62.7,117,0,117h217C151,117,146.2,0,108.5,0" fill="#69c6d3"></path>
</svg>
</div>
The problem is that when i hover last SVG, it animate the first one.
Can someone help me to change mouseenter/leave to work from $(this) ?
I'm going to suggest a way without using JQuery firstly, as it's not really necessary.
I would change your css to make the hover path display: none, rather than opacity: 0, as display none means that events will pass through fine, whereas opacity will capture them. Or you could reorder the paths, so the hove paths come first.
var normalWaves = Snap.selectAll('.svg-wave-normal');
var normalPoints = Snap.select('.svg-wave-normal').attr('d');
var hoverPoints = Snap.select('.svg-wave-hover').attr('d');
normalWaves.forEach(function( wave ) {
wave.mouseover(function() {
this.animate({
d: hoverPoints
}, 600);
})
.mouseout(function() {
this.animate({
d: normalPoints
}, 600);
});
});
svg .svg-wave-hover { display: none; }
jsfiddle
I am having a div with 3 elements. I want to convert that whole div into an image without using third party tool. Kindly provide me any suggestion regarding this.
<!DOCTYPE html>
<html>
<body>
<div id="div1">DIV 1<br>
<svg width="50" height="50">
<path d="M0,0 L50,0 L50,50 Z"
style="stroke: #006666; fill:none;"/>
</svg>
<div>DIV 2</div>
<input type="button" value="Button"/>
</div>
</body>
</html>
This is probably a good starting point. I have slightly modified the answer from this question https://stackoverflow.com/a/27232525/8085668. I have just removed the callback and added a click event. Please refer to it for a detailed explanation of the code.
var svgText = document.getElementById("myViewer").outerHTML;
var myCanvas = document.getElementById("canvas");
var ctxt = myCanvas.getContext("2d");
var btn = document.getElementsByClassName('convert2Png')[0];
function drawInlineSVG(ctx, rawSVG) {
var svg = new Blob([rawSVG], {type:"image/svg+xml;charset=utf-8"}),
domURL = self.URL || self.webkitURL || self,
url = domURL.createObjectURL(svg),
img = new Image;
img.onload = function () {
ctx.drawImage(this, 0, 0);
domURL.revokeObjectURL(url);
};
img.src = url;
}
// usage:
btn.addEventListener('click', function() {
drawInlineSVG(ctxt, svgText);
});
<!DOCTYPE html>
<html>
<body>
<div id="div1">DIV 1<br>
<svg width="50" height="50" id="myViewer" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<path d="M0,0 L50,0 L50,50 Z"
style="stroke: #006666; fill:none;"/>
</svg>
<div>DIV 2</div>
<input class="convert2Png" type="button" value="Button"/>
<canvas id="canvas" width=800 height=600 style="margin-top:10px;"></canvas>
</div>
</body>
</html>
I have got a inline svg as a variable on my page. I'm doing some modifications to it. How can I echo viewText on the page (not the svg image) with modifications. How can I make viewText show on the page?
Example:
<script>
var viewText = "<svg width="400" height="100"><rect width="400" height="100" style="fill:rgb(0,0,255);stroke-width:10;stroke:rgb(0,0,0)" /></svg>"
var rect = document.getElementsByTagName("rect");
for (var i = 0; i < rect.length; i++) {
//do something
}
document.write( viewText );//how do I output the modified 'viewText' code on the page?
/*e.g.:
<svg width="400" height="100"><rect width="400" height="100" style="fill:rgb(0,0,255);stroke-width:10;stroke:rgb(0,0,0)" /></svg> */
</script>
Something like this
<div id="target"></div>
<script>
var viewText = '<svg width="400" height="100"><rect width="400" height="100" style="fill:rgb(0,0,255);stroke-width:10;stroke:rgb(0,0,0)" /></svg>';
var rect = document.getElementsByTagName("rect");
for (var i = 0; i < rect.length; i++) {
//do something
}
document.getElementById('target').textContent = viewText;
</script>
Just add an id to your SVG element and use outerHTML:
<svg width="400" height="100" id="svg1">
<rect width="400" height="100" style="fill:rgb(0,0,255);stroke-width:10;stroke:rgb(0,0,0)" />
</svg>
<div id="target"></div>
var yourCode = document.getElementById("svg1").outerHTML;
document.getElementById("target").textContent=yourCode;
You will get your SVG as a string and you can put it in a div with id="target":
'<svg width="400" height="100" id="svg1">
<rect width="400" height="100" style="fill:rgb(0,0,255);stroke-width:10;stroke:rgb(0,0,0)"></rect>
</svg>'
I'm really confused here. I have a static SVG element that displays fine, but when I add an identical element from Javascript, it doesn't display. Why is this??
<html>
<head>
<script type="text/javascript">
function doit()
{
var svgdiv = document.getElementById('svg1');
for (var k = 1; k < 3; ++k)
{
var svg = document.createElement('svg');
svg.setAttribute('width',100);
svg.setAttribute('height',100);
console.log(svg);
var c = document.createElement('circle');
c.setAttribute('cx',50);
c.setAttribute('cy',50);
c.setAttribute('r',40);
c.setAttribute('stroke','green');
c.setAttribute('stroke-width',4);
c.setAttribute('fill','yellow');
svg.appendChild(c);
svgdiv.appendChild(svg);
}
}
window.onload = doit;
</script>
</head>
<body>
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
</svg>
<div id="svg1"></div>
</body>
</html>
Use
document.createElementNS('http://www.w3.org/2000/svg', 'svg')
instead of
document.createElement('svg')
Explanation:
SVG elements must be created in the SVG namespace and cannot therefore be created by createElement, instead you must use createElementNS providing the SVG namespace as the first argument.
createElement basically creates html elements called svg and circle rather than SVG elements.
text/html doesn't really have namespaces so the HTML parser magically switches to the SVG namespace when it encounters an <svg> element. If you changed the mime type to some XML namespace e.g. http://www.w3.org/1999/xhtml/ then you'd need an xmlns attribute on the root <html> element and also on the <svg> element.
<html>
<head>
<script type="text/javascript">
function doit()
{
var svgdiv = document.getElementById('svg1');
for (var k = 1; k < 3; ++k)
{
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width',100);
svg.setAttribute('height',100);
console.log(svg);
var c = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
c.setAttribute('cx',50);
c.setAttribute('cy',50);
c.setAttribute('r',40);
c.setAttribute('stroke','green');
c.setAttribute('stroke-width',4);
c.setAttribute('fill','yellow');
svg.appendChild(c);
svgdiv.appendChild(svg);
}
}
window.onload = doit;
</script>
</head>
<body>
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
</svg>
<div id="svg1"></div>
</body>
</html>
I'm creating a software that uses an embedded browser to show SVG files (HTML/HTML5 with SVG).
A lot of SVG files would have a lot in common, so I'd like to avoid copying that part to all SVG files. I'd like to provide a base SVG to be extended/included. Lets call the base SVG "base.svg".
I searched for something like INCLUDE in SVG but it doesn't exist.
Is it possible to create a HTML file that has an HTML5 link element, or javascript code to include the base.svg?
Then I'd simply put the rest of SVG in the HTML. Something like this (pseudo):
<html>
<body>
<link rel="import" href="base.svg" />
<!-- or some javascript code to include the base.svg content -->
<svg>
<!-- The rest of the SVG -->
</svg>
</body>
</html>
Is it possible?
If not, is it a good choice to use a PHP server (or something like this) to generate the complete SVG dynamically (result = base.svg + rest.svg)?
Thanks very much.
=== Solution (based on Francis Hemsher approach):
HTML file:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Combine/Parse SVG Files via XML DOM</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body style='padding:0px;font-family:arial'>
<center>
<h4>Combine/Parse SVG Files via XML DOM</h4>
<div style='width:90%;background-color:gainsboro;text-align:justify;padding:10px;border-radius:6px;'>
Load svg files as XML via <b>XMLHttpRequest</b>. Parse the Document Elements to create a client's file.
This example first loads the <b>Base.svg</b> inline into a DIV, then extracts/adds elements from another <b>companion</b> svg file,
combining them into a seamless svg root.
</div>
<br />
Base + Companion SVG:
<textarea id=svgValue style='width:90%;height:100px;font-size:120%;font-family:lucida console;'></textarea>
<div id="svgDiv" style='background-color:white;width:400px;height:400px;' ></div>
<br />
Parameters:
<br />
<textarea id=jsParams style='border-radius:26px;font-size:110%;font-weight:bold;color:midnightblue;padding:16px;background-color:beige;border-width:0px;font-size:100%;font-family:lucida console;width:90%;height:40px'></textarea>
<br />
Javascript:
<br />
<textarea id=jsValue style='border-radius:26px;font-size:110%;font-weight:bold;color:midnightblue;padding:16px;background-color:beige;border-width:0px;font-size:100%;font-family:lucida console;width:90%;height:400px'></textarea>
</center>
<script id="parameterRetrieval">
var params = new Array();
function queryParameters() {
var query = window.location.search.substring(1);
var lParams = query.split('&');
for (var i = 0; i < lParams.length; ++i) {
var pos = lParams[i].indexOf('=');
if (pos > 0) {
var key = lParams[i].substring(0, pos);
var val = lParams[i].substring(pos + 1);
params[key] = val;
console.log("params[ " + key + " ] = \"" + val + "\"");
}
}
}
function getParameterByName(key) {
return params[key];
}
</script>
<script id="SVGComposer">
var XMLdoc;
function loadCompanionXML() {
// Element inside the base SVG file to hold the companion SVG content.
var destElement = getParameterByName("destination");
// Companion SVG name.
var SVGFile = getParameterByName("svg");
if (SVGFile == null) {
console.log("Missing parameter: child svg file (svg).");
return;
}
var loadXML = new XMLHttpRequest;
function handler() {
if ((loadXML.readyState == 4) && (loadXML.status == 200)) {
var childSVG = document.getElementById(destElement);
if (childSVG != null) {
childSVG.innerHTML = loadXML.responseText;
svgValue.value=svgDiv.innerHTML;
} else {
console.log("Could not get the child SVG container.");
}
}
}
if (loadXML != null) {
loadXML.open("GET", SVGFile, true);
loadXML.onreadystatechange = handler;
loadXML.send();
}
}
document.addEventListener("onload", init(), false);
function init() {
queryParameters();
var SVGFile = "base.svg";
var loadXML = new XMLHttpRequest;
function handler() {
if ((loadXML.readyState == 4) && (loadXML.status == 200)) {
svgDiv.innerHTML = loadXML.responseText;
loadCompanionXML();
}
}
if (loadXML != null) {
loadXML.open("GET", SVGFile, true);
loadXML.onreadystatechange = handler;
loadXML.send();
}
jsValue.value = SVGComposer.text;
}
</script>
</body>
</html>
base.svg:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="w3.org/1999/xlink" id="baseSVG" width="400" height="400" fill="white">
<circle r="50" fill="green" cx="50" cy="50" stroke="none" />
<g id="childSVG" />
</svg>
companion.svg:
<defs>
<image id="bkimage" xlink:href="http://placepuppy.it/320/240" width="100%" height="100%" />
<rect id="MyRect" width="100" height="100" stroke="black" stroke-width="2" />
</defs>
<g>
<defs>
<mask id="image1mask" ><polygon points="0,0, 320,0, 160,120, 320,240, 0,240" fill-opacity="30%" /></mask>
</defs>
<use xlink:href="#bkimage" mask="url(#image1mask)"/>
</g>
<use xlink:href="#MyRect" x="100" y="100" fill="red" />
<use xlink:href="#MyRect" x="150" y="150" fill="blue" />
<use xlink:href="#MyRect" x="200" y="200" fill="green" />
<g>
<defs>
<clipPath id="MyRectClip"><rect x="270" y="270" width="60" height="60" /></clipPath>
</defs>
<rect x="250" y="250" width="100" height="100" stroke="black" stroke-width="2" fill="yellow" clip-path="url(#MyRectClip)"/>
</g>
To load it, you should open the following URL:
http://theHTMLfile?svg=companion.svg&destination=childSVG
Ps: I know the coordinates of the image mask are not working very well. Ignore that. It was just a test.
Cheers!
I assume you have access to the XML DOM. If so, the best way to merge the svg is via xml dom methods.
EDIT (added below) UPDATE: added xlink namespace, needed for image elements
Below is a very basic means of combining svg files into a single inline svg on the client. It it can Combine/Parse SVG Files via XML DOM.
Loads svg files as XML via XMLHttpRequest. Parse the Document Elements to create a client's file.
This example first loads the Base.svg inline into a DIV, then extracts/adds elements from another companion.svg file,
combining them into a seamless svg root.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Combine/Parse SVG Files via XML DOM</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body style='padding:0px;font-family:arial'>
<center><h4>Combine/Parse SVG Files via XML DOM</h4>
<div style='width:90%;background-color:gainsboro;text-align:justify;padding:10px;border-radius:6px;'>
Load svg files as XML via <b>XMLHttpRequest</b>. Parse the Document Elements to create a client's file.
This example first loads the <b>Base.svg</b> inline into a DIV, then extracts/adds elements from another <b>companion</b> svg file,
combining them into a seamless svg root.
</div>
<br />Base + Companion SVG:
<textarea id=svgValue style='width:90%;height:200px;font-size:120%;font-family:lucida console;'></textarea>
<div id="svgDiv" style='background-color:lightgreen;width:400px;height:400px;' ></div>
<br />Javascript:<br />
<textarea id=jsValue style='border-radius:26px;font-size:110%;font-weight:bold;color:midnightblue;padding:16px;background-color:beige;border-width:0px;font-size:100%;font-family:lucida console;width:90%;height:400px'></textarea>
</center>
<script id=myScript>
/*
Base.svg
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="baseSVG" width="400" height="400">
<circle r="50" fill="green" cx="100" cy="100" stroke="none" />
</svg>
companion.svg
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="companionSVG">
<rect x="180" y="200" width="200" height="150" fill="red" />
<ellipse cx="70" cy="300" rx="65" ry="25" fill="blue" />
<image x="120" y="100" xlink:href="myImage.png" width="198" height="65" />
</svg>
*/
//--onload---
function loadBaseSVGInline()
{
var SVGFile="Base.svg"
var loadXML = new XMLHttpRequest;
function handler(){
if(loadXML.readyState == 4 && loadXML.status == 200)
{
svgDiv.innerHTML=loadXML.responseText
loadCompanionXML()
}
}
if (loadXML != null){
loadXML.open("GET", SVGFile, true);
loadXML.onreadystatechange = handler;
loadXML.send();
}
}
var XMLdoc
function loadCompanionXML()
{
var SVGFile="companion.svg"
var loadXML = new XMLHttpRequest;
function handler()
{
if(loadXML.readyState == 4 && loadXML.status == 200)
{
//---responseText---
var xmlString=loadXML.responseText
//---DOMParser---
var parser = new DOMParser();
XMLdoc=parser.parseFromString(xmlString,"text/xml").documentElement;
companionXML2Base()
}
}
if (loadXML != null){
loadXML.open("GET", SVGFile, true);
loadXML.onreadystatechange = handler;
loadXML.send();
}
}
//--place companion elements into base
function companionXML2Base()
{
var mySVG=document.getElementsByTagName("svg")[0]
var elems=XMLdoc.childNodes
for(var k=0;k<elems.length;k++)
{
var elem=elems.item(k).cloneNode(true)
mySVG.appendChild(elem)
}
svgValue.value=svgDiv.innerHTML
}
</script>
<script>
document.addEventListener("onload",init(),false)
function init()
{
loadBaseSVGInline()
jsValue.value=myScript.text
}
</script>
</body>
</html>