{"id":819,"date":"2012-12-13T09:53:41","date_gmt":"2012-12-13T13:53:41","guid":{"rendered":"http:\/\/blogs.law.harvard.edu\/jreyes\/?p=819"},"modified":"2012-12-23T15:50:46","modified_gmt":"2012-12-23T19:50:46","slug":"render-d3-js-driven-svg-server-side","status":"publish","type":"post","link":"https:\/\/archive.blogs.harvard.edu\/jreyes\/2012\/12\/13\/render-d3-js-driven-svg-server-side\/","title":{"rendered":"Render D3.js-driven SVG server-side!"},"content":{"rendered":"<p>Recently I&#8217;ve been working on a <a href=\"http:\/\/congress.joshreyes.com\">congressional tweet aggregator<\/a> to get a handle on what our legislators are saying. To make that easier to see, I figured I&#8217;d start adding some charts and lists and other snazzy dataviz gizmos that are so hot these days.<\/p>\n<p>I like <a href=\"http:\/\/www.d3js.org\">D3.js<\/a> as a graphing library. It makes clean, interactive, data-driven charts a snap to make in just a few lines of Javascript. Then it does its magic to render the data in crisp SVG, which I am quite fond of. On my site, I wanted to turn the crank on the back-end for charts that don&#8217;t update all that frequently, inject them into my templates, and spare the viewers of my site the heavy-lifting required for multiple charts&mdash;not to mention my poor server that has to execute several complicated queries to do the appropriate counting to generate the data to back the charts.<\/p>\n<p>After a little poking around on the internet, I stumbled on to <a href=\"http:\/\/phantomjs.org\/\">PhantomJS<\/a>, which bills itself as a full-stack headless WebKit. Perfect. It can ping my website periodically, load the chart pages, and extract the SVG, I thought.<\/p>\n<p>Not so fast. The Phantom is excellent at reading SVG, and it&#8217;s even good at rendering it to PDF or PNG. But that&#8217;s not what I wanted! I just wanted it to spit out the SVG for me after D3 was finished making it, untouched. And since SVG elements don&#8217;t have an innerHTML property, I needed to think harder to find a solution; i.e., ask Google. But Google didn&#8217;t seem to know, either. So I wrote a tiny script to extract page elements by ID. Maybe one of you will find it useful, too.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar system = require(&#039;system&#039;);\r\n\r\nif (system.args.length != 3) {\r\n    console.log(&quot;Usage: extract.js  &quot;);\r\n    phantom.exit(1);\r\n}\r\n\r\nvar address = system.args&#x5B;1];\r\nvar elementID = system.args&#x5B;2];\r\nvar page = require(&#039;webpage&#039;).create();\r\n\r\nfunction serialize(elementID) {\r\n    var serializer = new XMLSerializer();\r\n    var element = document.getElementById(elementID);\r\n    return serializer.serializeToString(element);\r\n}\r\n\r\nfunction extract(elementID) {\r\n  return function(status) {\r\n    if (status != &#039;success&#039;) {\r\n      console.log(&quot;Failed to open the page.&quot;);\r\n    } else {\r\n      var output = page.evaluate(serialize, elementID);\r\n      console.log(output);\r\n    }\r\n  phantom.exit();\r\n  };\r\n}\r\n\r\npage.open(address, extract(elementID));\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Recently I&#8217;ve been working on a congressional tweet aggregator to get a handle on what our legislators are saying. To make that easier to see, I figured I&#8217;d start adding some charts and lists and other snazzy dataviz gizmos that &hellip; <a href=\"https:\/\/archive.blogs.harvard.edu\/jreyes\/2012\/12\/13\/render-d3-js-driven-svg-server-side\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":102,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[380,142],"tags":[],"class_list":["post-819","post","type-post","status-publish","format-standard","hentry","category-computer-science","category-technology"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/archive.blogs.harvard.edu\/jreyes\/wp-json\/wp\/v2\/posts\/819","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/archive.blogs.harvard.edu\/jreyes\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/archive.blogs.harvard.edu\/jreyes\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/archive.blogs.harvard.edu\/jreyes\/wp-json\/wp\/v2\/users\/102"}],"replies":[{"embeddable":true,"href":"https:\/\/archive.blogs.harvard.edu\/jreyes\/wp-json\/wp\/v2\/comments?post=819"}],"version-history":[{"count":4,"href":"https:\/\/archive.blogs.harvard.edu\/jreyes\/wp-json\/wp\/v2\/posts\/819\/revisions"}],"predecessor-version":[{"id":823,"href":"https:\/\/archive.blogs.harvard.edu\/jreyes\/wp-json\/wp\/v2\/posts\/819\/revisions\/823"}],"wp:attachment":[{"href":"https:\/\/archive.blogs.harvard.edu\/jreyes\/wp-json\/wp\/v2\/media?parent=819"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/archive.blogs.harvard.edu\/jreyes\/wp-json\/wp\/v2\/categories?post=819"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/archive.blogs.harvard.edu\/jreyes\/wp-json\/wp\/v2\/tags?post=819"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}