SVG <use> with JS variables

Discuss SVG code, accessible via the XML Editor.
JimJoyce
Posts: 37
Joined: Fri Jun 19, 2009 7:06 pm

SVG <use> with JS variables

Postby JimJoyce » Fri Nov 18, 2011 11:18 pm

I'm trying to build a graphic repeating and rotating a <def>.
The graphic is horseshoe shaped, a sort of five-sided hexagon !!!

I think I've simplified the task by using only one iteration,
and by combining 3 functions in init().
I've also peppered the script with alerts.

Can anyone help?

Code: Select all

<?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"  baseProfile="full"
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  width="480"  height="480"
  onload="init(evt)">

<defs> <polygon id="hex"
  points= "60, 16  44,-44  -16,-60  -60,-16  -44, 44
   16,60  5,70  9,86  -66,66  -90,-24 
   -24,-90  66,-66  86,9  70,5" />
</defs>

<script type="text/ecmascript">
<![CDATA[

var svgns="http://www.w3.org/2000/svg";
var sns = "http://www.w3.org/2000/xlink/namespace";
var xns = "http://www.w3.org/1999/xlink";
var xrf = "xlink:href";
var svg;
var tgt;
var svgdoc;
var grp;
var hx;
var x = 335;
var y  335;
var clr = "#0f0";
var rot = 120;
// Following are used only in ALERTS
var par, xrf,  xx, yy, frst, sty, trnsf;

function init(evt) {
  tgt = evt.target;
  if(window.svgDocument == null)
    { svgdoc = tgt.ownerDocument; }
alert("SVGDOC="+svgdoc);

  grp=document.createElementNS(svgns,'g');
alert("GROUP="+grp);

//  svgdoc.appendChild(grp);

//  par = getParentNode(grp);
//alert("PARENT="+par);

  hx = document.createElementNS(svgns,'use');
alert("USE="+hx);

  hx.setAttribute("x",x);
  xx = hx.getAttribute("x");
  hx.setAttribute("y",y);
  yy = hx.getAttribute("y");
alert("X,Y="+xx+','+yy);

  hx.setAttribute("xlink:href","#hex");
  xrf = hx.getAttribute("xlink:href");
alert("XREF="+xrf);

  hx.setAttribute("style",
    "stroke: #000; stroke-width: 1; fill: clr;");
  styl = hx.getAttribute("style");
alert("STYLE="+styl);
  fil = hx.getAttribute("style.fill");
alert("STYLE.FILL="+styl.fill);
  strok = hx.getAttribute("style.stroke");
alert("STYLE.STROKE="+strok);
  swdth = hx.getAttribute("style.stroke-width");
alert("STYLE.WIDTH="+styl.swdth);

  hx.setAttribute("transform","rotate(rot)");
  trnsf = hx.getAttribute("transform");
alert("TRNSF="+trnsf);

  trot = hx.getAttribute("transform.rotate");
alert("TRNSF.ROT="+trot);

  grp.appendChild(hx);
  frst = getFirstChild(grp);
alert("FIRST="+frst);
  }

// ]]>
</script>
</svg>


After creating the group <g>,
grp=document.createElementNS(svgns,'g');
alert("GROUP="+grp);

I tried to append it to svgdoc
// svgdoc.appendChild(grp);
That failed. I assume because it is already attached ???
So it's commented out.

So I tried to inspect it's parent.. That also failed.
// par = getParentNode(grp);
//alert("PARENT="+par);
Also commented out.

After that I inspect every step with an alert.
( The sub-attributes of my 'style' and 'transform'
seem particularly strange.) (I may be mis-coding them.)

Right at the end, I try to append my 'hex' (the horseshoe graphic)
to the <g> group.
grp.appendChild(hx);
It crashes, No graphic appears

Moreover, grp appears to have no children.
frst = getFirstChild(grp);
alert("FIRST="+frst);

I don't know where to start.
Please don't tell me 'Start Again!'.

JimJoyce
Posts: 37
Joined: Fri Jun 19, 2009 7:06 pm

Re: SVG <use> with JS variables

Postby JimJoyce » Sat Nov 19, 2011 9:48 pm

Not really a Reply: just a simplification of the 90 lines of code above.

The function init() really has only 10 lines of processing:

[quote]
svgdoc = evt.tatget.ownerDocument;
grp=document.createElementNS(svgns,'g');
svgdoc.appendChild(grp);

hx = document.createElementNS(svgns,'use');
hx.setAttribute("xlink:href","#hex");
hx.setAttribute("x",x);
hx.setAttribute("y",y);
hx.setAttribute("style",
"stroke: #000; stroke-width: 1; fill: clr;");
hx.setAttribute("transform","rotate(rot)");

grp.appendChild(hx);
[/quote}

3 lines to establish the connection to SVG ??
(These don't work.)

6 lines to build the SVG object.

1 line to attach the object to the root
(Which failed to be created)

Can anyone tell me:
What is wrong with the first 3 lines ?

chriswww
Posts: 383
Joined: Fri Nov 19, 2010 3:04 pm

Re: SVG <use> with JS variables

Postby chriswww » Mon Nov 21, 2011 9:31 am

i believe there's a special way to get at the svg document. try something like documented at cartonet http://www.carto.net/svg/manipulating_s ... cmascript/

JimJoyce
Posts: 37
Joined: Fri Jun 19, 2009 7:06 pm

Re: SVG <use> with JS variables

Postby JimJoyce » Mon Nov 21, 2011 11:09 pm

Thanks chrisswww, Carto.net's article is particularly helpful. I also found KevLinDev.com to be very explicit.
I'v simplified the code yet again, removing the transform and also the variable in style-fill.

FF, IE and Chrome accept the code, flagging no errors,
yet fail to reach the alert() at the beginning of init(), my onload-function.
Opera tells me init is an undefined variable.

<?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"
baseProfile="full"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="480" height="480"
onload="init(evt);">

<script type="image/svg+xml">
<![CDATA[

var svgns="http://www.w3.org/2000/svg";
var sns="http://www.w3.org/2000/xlink/namespace";
var xns="http://www.w3.org/1999/xlink";
var xrf="xlink:href";
var svgDocument,svgdoc;
var def, hex, use;
var x=335, y=335;

function init(evt)
{
alert('Loading');
if ( window.svgDocument == null )
window.svgDocument = evt.target.ownerDocument;
svgdoc = window.svgDocument;

def = document.createElementNS(svgns,"defS");
hex = document.createElementNS(svgns,"polygon");
use = document.createElementNS(svgns,"use");

hex.setAttribute("points",
"60,16 44,-44 -16,-60 -60,-16 -44,44 16,60 5,70
9,86 -66,66 -90,-24 -24,-90 66,-66 86,9 70,5");
def.appendChild(hex);
svgdoc.appendChild(def);

use.setAttributeNS(svgns,xrf,"#hex");
use.setAttribute("x",x);
use.setAttribute("y",y);
use.setAttribute("style",
"stroke: #000; stroke-width: 1; fill: green;");

svgdoc.appendChild(use);
}

// ]]>
</script>
</svg>

I'll keep on trying, I'll discover a trivial error in the end.
Thanks for your guidance.

User avatar
tomh
Posts: 218
Joined: Sat Feb 14, 2009 10:14 pm

Re: SVG <use> with JS variables

Postby tomh » Tue Nov 22, 2011 11:44 am

First mistake (in the new version);

Code: Select all

<script type="image/svg+xml">
(line 12)

is incorrect... it should be:

Code: Select all

<script type="text/ecmascript">
.

I think that is a big one ;-)

Second. the if statement is incorrect; it requires

Code: Select all

{}
brackets around the statement to be performed

Oh, and the word "use" seems to be a reserved word in javascript, so I don't think you can use it as a variable name???

(Not that this completely fixes the issues, but it should give you a start!)

JimJoyce
Posts: 37
Joined: Fri Jun 19, 2009 7:06 pm

Re: SVG <use> with JS variables

Postby JimJoyce » Tue Nov 22, 2011 8:46 pm

Thanks tomh,
I'm not so sure about the text/ecmascript. I had that originally. (I cannot remember on which version.
You are right about the missing curly-braces. Script expects them.
As for 'use'. I've seen it used before (not that I'm sure it worked). I've changed it to 'yus'

I've made all three changes. Opera still says init is an undefined variable.
And as before FF, IE and Chrome still silently do nothing.

Thanks, it's great to have helpful feedback.

User avatar
tomh
Posts: 218
Joined: Sat Feb 14, 2009 10:14 pm

Re: SVG <use> with JS variables

Postby tomh » Wed Nov 23, 2011 12:42 am

Defiantly text/ecmascript ...

Also when I run it through firebug (getfirebug.com) for firefox; line 50 says this:

Node cannot be inserted at the specified point in the hierarchy
[Break On This Error] svgdoc.appendChild(usea);
js_test.svg (line 50)

so I believe that this is why carto.net uses this

Code: Select all

      document.getElementById("firstGroup").appendChild(newRect);


so you aren't trying to add an element on the same level as the root of svg file.

chriswww
Posts: 383
Joined: Fri Nov 19, 2010 3:04 pm

Re: SVG <use> with JS variables

Postby chriswww » Wed Nov 23, 2011 9:35 am

regards the undefined init..that's what happens with javascript/ecmascript. you need to have the function definition being seen by the parser first, before the parser sees the reference to it for the onload. there a several ways around this parser hindrance, one of which is to assign the onload event handler using javascript, after the function definition. directly assigning event handler to onload inline as you have, is probably not the preferred way anyway...as there are some race condition and browser issues involved.

User avatar
tomh
Posts: 218
Joined: Sat Feb 14, 2009 10:14 pm

Re: SVG <use> with JS variables

Postby tomh » Wed Nov 23, 2011 10:48 am

Well, I have managed to adapt the second version to a working state ;-) , the couple of problems I found was not setting all the attributes by using the name space reference and a couple of other things. I ended up returning to the original def section rather than creating it in javascript. Trying to create 3 elements before creating one seemed a bit ambitious :-)


<?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" baseProfile="full"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="480" height="480" onload="init(evt)">
<script type="text/ecmascript">
<![CDATA[
var svgns="http://www.w3.org/2000/svg";
var xns="http://www.w3.org/1999/xlink";
var use;
var x=660, y=435;

function init(evt)
{
alert('Loading');

use = document.createElementNS(svgns,"use");
use.setAttributeNS(xns,"href","#hex");
use.setAttributeNS(svgns,"x",x);
use.setAttributeNS(svgns,"y",y);
use.setAttributeNS(svgns,"style", "stroke: #000; stroke-width: 1; fill: green;");
document.getElementById("FirstGroup").appendChild(use);

alert('Finnished');
}

// ]]>
</script>
<defs>
<polygon id="hex"
points= "60, 16 44,-44 -16,-60 -60,-16 -44, 44
16,60 5,70 9,86 -66,66 -90,-24
-24,-90 66,-66 86,9 70,5" />
</defs>
<g id="FirstGroup"></g>
</svg>


chriswww
Posts: 383
Joined: Fri Nov 19, 2010 3:04 pm

Re: SVG <use> with JS variables

Postby chriswww » Wed Nov 23, 2011 3:33 pm

onwards and upwards ;)
for the next bit: create infographics and graphs according to Edward Tufte design criteria...programmatically. Only kidding. Would be nice to be able to do.

JimJoyce
Posts: 37
Joined: Fri Jun 19, 2009 7:06 pm

Re: SVG <use> with JS variables

Postby JimJoyce » Wed Nov 23, 2011 9:15 pm

First: Apologies to Chrisswww. You told me look at Carto.net.
I did, but not too seriously. I thought I knew all that stuff !!!.
It was only when TomH referred to 'document.getElementById("FirstGroup")'
that I really searched. Even then I missed both references as I was expecting it
to follow somewhere after the declaration of FirstGroup.

Last night I worked my way through Carto.net.
Example 6 was an eye-opener. He handles a Rect, a Text and a Use,
just three critical examples, all in one file.
I realised I needed a Group, outside of the script,
which would automatically attached to the svg document.
Also I'd made a complete hash of the xlink reference in my <use>.
I pencilled out a completely new version.

Now, this morning, I find TomH's complete solution.
Thanks, tomh.
One small correction: your x and y values place the shape outside my 480x480 area.
I have it working perfectly. Except I'm puzzled at the scale.
It appears to be about four times my expected size.

I'm happy. I'll append my final version when I've added the rotation,
colouring,and six-times iteration..
Thanks all who've helped.
JJ

JimJoyce
Posts: 37
Joined: Fri Jun 19, 2009 7:06 pm

Re: SVG <use> with JS variables

Postby JimJoyce » Wed Nov 23, 2011 9:47 pm

Sorry, just a footnote.
I was viewing TomH's work in Opera, my favourite for SVG.
That's where the image is over-large.
I've since tried FF, IE and Chrome.
At first FF showed a normal-sized image, disappearing, top-left.
It has since disappeared completely !!
IE & Chrome both show it disappearing top-left.
Maybe TomH's x and y were more appropriate.

JimJoyce
Posts: 37
Joined: Fri Jun 19, 2009 7:06 pm

Re: SVG <use> with JS variables

Postby JimJoyce » Wed Nov 23, 2011 9:56 pm

Sorry, again.
I've just spotted my Opera's Zoom-control, bottom-left, is at full magnification

JimJoyce
Posts: 37
Joined: Fri Jun 19, 2009 7:06 pm

Re: SVG <use> with JS variables

Postby JimJoyce » Wed Nov 23, 2011 11:58 pm

Hi Folks,
I think I've got what I wanted.
Thanks to you all.

Code: Select all

<?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"
      baseProfile="full"
      xmlns      ="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      width="480" height="480" >

<script type="text/ecmascript">
<![CDATA[

  var   svgNS = "http://www.w3.org/2000/svg";
  var   lnkNS = "http://www.w3.org/1999/xlink";
  var   xrf   = "xlink:href";
  var   poly, def, hex, grp, yus;
  var   x = [275,370,335,205,110,145];
  var   y = [110,205,335,370,275,145];
  var rot = [,60,120,180,240,300];
//  var ids = ['p0','p1','p2','p3','p4','p5'];
  var colr = ['#f00','#ff0','#0f0','#0ff','#00f','#f0f'];


function createUse(k)
  {
alert("createUse k="+k);
  yus = document.createElementNS(svgNS,'use');
  yus.setAttributeNS(lnkNS,xrf,"#hex");
  yus.setAttribute("x",x[k]);
  yus.setAttribute("y",y[k]);
  yus.setAttribute("style","stroke: none; fill: "+colr[k]+";" );
  yus.setAttribute("transform","rotate("+rot[k]+','+x[k]+','+y[k]+")" );
  document.getElementById("wrap").appendChild(yus);
  }

function iterate(z)
  {
  for (k=0 ; k<z ; k++ )
    { createUse(k); }
  }

// ]]>
</script>

  <defs>
    <polygon id="hex" points=
      "60,16 44,-44 -16,-60 -60,-16 -44,44 16,60 5,70 9,86 -66,66 -90,-24 -24,-90 66,-66 86,9 70,5" />
  </defs>

  <g id="wrap">
    <script type="text/ecmascript">
      iterate(6);
    </script>
  </g>

</svg>

Thanks, JJ

JimJoyce
Posts: 37
Joined: Fri Jun 19, 2009 7:06 pm

Re: SVG <use> with JS variables

Postby JimJoyce » Thu Nov 24, 2011 3:17 am

I think this is my final message on this topic.
I can now iterate 6, 12 18 and 24 times.
Variables x, y, rot and colr need to be changed,
as well as the function-call iterate(?);

Code: Select all


      iterate(6);
  var   x = [275,370,335,205,110,145];
  var   y = [110,205,335,370,275,145];
  var rot = [0,60,120,180,240,300];
  var colr = ['#f00','#ff0','#0f0','#0ff','#00f','#f0f'];
      iterate(6);

      iterate(12);
  var   x = [275,335,370,370,335,275,205,145,110,110,145,205];
  var   y = [110,145,205,275,335,370,370,335,275,205,145,110];
  var rot = [0,30,60,90,120,150,180,210,240,270,300,330];
  var colr = ['#f00','#f80','#ff0','#8f0','#0f0','#0f8',
              '#0ff','#08f','#00f','#80f','#f0f','#f08'];
      iterate(12);

      iterate(18);
  var x = [275,317,349,370,374,361,335,297,252,
           205,163,131,110,106,119,145,183,228];
  var y = [110,131,163,205,252,297,335,361,374,
           370,349,317,275,228,183,145,119,106];
  var rot = [0,20,40,60,80,100,120,140,160,180,
             200,220,240,260,280,300,320,340];
  var colr = ['#f00','#f50','#fa0','#ff0','#af0','#5f0',
              '#0f0','#0f5','#0fa','#0ff','#0af','#05f',
              '#00f','#50f','#a0f','#f0f','#f0a','#f05'];
      iterate(18);

      iterate(24);
  var   x = [275,307,335,355,370,374,370,356,335,307,275,240,
           205,173,145,124,110,106,110,124,145,173,205,240];
  var   y = [110,124,145,173,205,240,275,307,335,356,370,374,
           370,356,335,307,275,240,205,173,145,124,110,106];
  var rot = [0,15,30,45,60,75,90,105,120,135,150,165,180,
             195,210,225,240,255,270,285,300,315,330,345];
  var colr = ['#f00','#f40','#f80','#fc0','#ff0','#cf0',
              '#8f0','#4f0','#0f0','#0f4','#0f8','#0fc',
              '#0ff','#0cf','#08f','#04f','#00f','#40f',
              '#80f','#c0f','#f0f','#f0c','#f08','#f04'];
      iterate(24);


Thanks, and Bye,
JJ


Return to “SVG / XML Code”