3

If the object tag is not really suitable, then is there a more preferable method for grabbing an SVG file and manipulating it directly with two.js? Or will I have to go into the SVGs and just add them inline?

Using this question's code and some of my own edits I can't get my object to rotate, Chrome gives me the error: Uncaught TypeError: Cannot read property 'x' of undefined

HTML

<object type="image/svg+xml" data="./images/face.svg" id="face"></object>

JS

    $(function(){  
    var svgObject = document.getElementsByTagName('object')[0];
        svgObject.onload = function(){
            var mySvg = svgObject.contentDocument.getElementsByTagName('svg')[0];
            var two = new Two({
                fullscreen:false,
                autostart: true
            }).appendTo(document.body);
            var shape = two.interpret(mySvg);
            console.log(shape);
            two.update();
            two.bind('update', function() {
                shape.rotation += 0.01;
            });
        };
    });

Since the code in the referenced question seems to have worked for the individual, I assume I just have a syntax error. Any ideas as to what's going on?

EDIT: With Francis' help, I was able to get my own SVG parsed for use with the for loop Francis mentioned in order to access the elements inside, but it seems to be considering the nested G tag within the following SVG to be null...for some reason:

<svg id="svg2" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="280" width="280" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><g id="layer1" transform="matrix(0.99999301,0,0,1,-400.71477,-954.83987)"><g id="g3836" transform="matrix(-0.08567939,-0.08567939,0.08567939,-0.08567939,574.3717,1199.1604)"><path id="path3801" fill="#3b5998" d="m805.17-1221.6c-902.43,902.43-902.43,2365.6,0.00003,3268l517.13-517.1c-616.83-616.83-616.83-1616.9,0-2233.8z"/><path id="rect3822" fill="#FFF" d="m266.93,336.2c0.15806,60.722,23.582,102.66,103.93,102.66h44.694v62.567h71.844v-62.57h184.55v-73.089h-184.55v-62.849l-71.844-8.5959v71.445h-44.69c-20.309,0-35.863-7.2042-35.863-35.676v-38.658h-64.869c-2.0284,15.729-3.2307,30.752-3.1941,44.765z"/></g></g></svg>

Also, Chrome throws the following errors on load:

'Uncaught TypeError: Cannot read property 'nodeName' of null' on line 68, in the handler() function (this is what I assumed to be the nested G being considered null):

xmlDocValue.value+=XMLdoc.childNodes.item(1).nodeName +"\n"

'Uncaught TypeError: Cannot read property 'childNodes' of undefined' in the aforementioned for loop, which I assume is because of the fact that the null element is considered null:

myGElems = XMLdoc.childNodes.item(0).childNodes
Community
  • 1
  • 1

1 Answers1

0

The parent html cannot access the <object>'s content. I assume you want to access the svg's elements in your file, and use their nodeName and attributes to create two.js elements. If so, The best way would be to use XMLHttpRequest to load the svg as XML and then DOMParser to get nodeName and attriutes of the elements. The responseText can also be loaded directly into a DIV's innerHTML.

Try this:

create an SVG file, called mySVG.svg as below:

<svg width="400" height="400" id="mySVG" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><circle id="myCircle"   r="100" cx="150" cy="150" fill="red" /><rect id="myRect" x="200" y="260" width="120" height="100" fill="blue" /></svg>

Then run the following HTML file:

   <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>Parse SVG Elements 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>Parse SVG Elements 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>. use <b>DOMParser</b> to parse the XML Document Element to create a client's svg elements.
    </div>
    <br />mySVG.svg:
    <textarea id=svgValue style='width:90%;height:200px;font-size:120%;font-family:lucida console;'></textarea>
    <center><b>SVG Inline</b></center>
    <div id="svgDiv" style='background-color:lightgreen;width:400px;height:400px;' ></div>
    <br /><button onClick=loadSVGXML()>load svg as xml</button><br />
    <center><b>XML Doc</b></center>
    <textarea id=xmlDocValue style='width:90%;height:100px;font-size:120%;font-family:lucida console;'></textarea>
    </center>
    <script id=myScript>
    /*
    mySVG.svg
    <svg id="mySVG"  version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><circle id="myCircle"   r="100" cx="150" cy="150" fill="red" /><rect id="myRect" x="200" y="260" width="120" height="100" fill="blue" /></svg>
    */
    //--onload---
    function loadSVGInline()
    {
        var SVGFile="mySVG.svg"
        var loadXML = new XMLHttpRequest;
        function handler(){
            if(loadXML.readyState == 4 && loadXML.status == 200)
            {
                svgDiv.innerHTML=loadXML.responseText
                svgValue.value=loadXML.responseText
            }
        }
        if (loadXML != null){
            loadXML.open("GET", SVGFile, true);
            loadXML.onreadystatechange = handler;
            loadXML.send();
        }
    }


/* NOTE:
If you 'hand-craft' an  svg file it may include carriage returns, or extraneous spaces between elements, the xml dom will interpret
them as #text elements, and may cause errors. Either remove carriage returns and spaces, or filter the elements
to ignore the #text elements.
*/

    var  XMLdoc
    function loadSVGXML()
    {
        var SVGFile="mySVG.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;

                xmlDocValue.value=XMLdoc.childNodes.item(0).nodeName +"\n"
                xmlDocValue.value+=XMLdoc.childNodes.item(0).getAttribute("fill") +"\n"
                xmlDocValue.value+=XMLdoc.childNodes.item(1).nodeName +"\n"
                xmlDocValue.value+=XMLdoc.childNodes.item(1).getAttribute("fill")

                //---to add xml element as svg use element.cloneNode(true)---
                var xmlNode1=XMLdoc.childNodes.item(1)
                var svgNode1=xmlNode1.cloneNode(true)
                svgNode1.setAttribute("x",20)
                svgNode1.setAttribute("fill","orange")
                mySVG.appendChild(svgNode1)
            }
        }
        if (loadXML != null){
            loadXML.open("GET", SVGFile, true);
            loadXML.onreadystatechange = handler;
            loadXML.send();
        }
    }
    </script>
    <script>
    document.addEventListener("onload",init(),false)
    function init()
    {
        loadSVGInline()
    }
    </script>
    </body>
    </html>
Francis Hemsher
  • 3,478
  • 2
  • 13
  • 15
  • Running your HTML works fine for your supplied SVG, and gives me tagNames I'd expect to see. but when using my own SVG I only get "#text" in the XML doc box (don't know why that's showing up since I have no text or any objects with said tag in my SVG), and Chrome throws: 'Uncaught TypeError: Object # has no method 'getAttribute' (index):59 handler' as an error. Thanks for your help! – Noel Quiles Apr 14 '14 at 03:26
  • When you 'hand-craft' svg files, you tend to use carriage returns to make it pretty. These are interpreted as #text elements. Therefore remove carriage returns or filter out the #text element. – Francis Hemsher Apr 14 '14 at 16:27
  • @EnMod: Also, spaces between elements will cause #text elements. – Francis Hemsher Apr 14 '14 at 17:07
  • Thanks I had no idea about that, after putting all of the elements on what was essentially one line in the SVG your parser appears to have parsed it correctly. However, what appears in the XML doc box is g (which I think I should expect) and null, and that's it. What exactly does that mean? – Noel Quiles Apr 15 '14 at 02:15
  • I would expect that means your svg has a single element with a bunch of elements contained in the . Therefore myGElems=XMLdoc.childNodes.item(0).childNodes Then just iterate through the myGElems to access them. e.g. for(var k=0;k – Francis Hemsher Apr 15 '14 at 16:26
  • Updated question with some more info...my apologies for leaving that out after my previous comment. – Noel Quiles Apr 18 '14 at 13:57
  • Also thank you for that snippet, I think it will prove useful; I'm extremely new at accessing external objects using JS, even more so with SVG. Please see my edit for the latest issue I'm having: this goes to anyone reading this. – Noel Quiles Apr 26 '14 at 05:07
  • This is slightly tangential, but if you have jQuery it's pretty easy to asynchronously load an svg file: `$.get('path-to-file.svg', function(resp) { var result = two.interpret($(doc).find('svg')[0]); });` as in this: http://jonobr1.github.io/two.js/examples/animate-stroke.html – jonobr1 May 01 '14 at 21:18
  • Wow, the creator of two.js himself? Good to see you. there have been some developments since my last comment. Some new info: -My SVGs are Optimized SVGs from Inkscape -When I use my SVGs with the samples from two.js (by replacing the sample SVG code with my own), there are JS errors: c is undefined, v is undefined...I have a feeling this has to do with the Optimized SVG exporting from Inkscape. -A sample SVG of mine that doesn't work with your examples, throwing errors, is [here](http://pastebin.com/4pNkC21J) I'm at my wit's end...weren't there issues with Inkscape Optimized SVGs? – Noel Quiles May 20 '14 at 03:36