How can I run a javascript as a node.js script without the DOM?

https://github.com/jasondavies/d3-cloud is a word cloud in javascript using D3 library. This is an interactive demo. The demo used the d3-cloud script to generate an SVG in the HTML.

I would like to use this on the server side and get node.js to generate me an SVG file and save it to the disk whenever I update my data instead of loading the D3 library to the browser and rendering it there. Is there a way to do this? Can I use libraries that seem to rely on the HTML DOM without the DOM?

I am new to node.js

Answers:

Answer

Check out jsdom. You can get access to a window context which you can then perform your d3 operations. Then use node file to save it to a filesystem

// Count all of the links from the Node.js build page
var jsdom = require("jsdom");

jsdom.env(
  "http://nodejs.org/dist/",
  ["http://code.jquery.com/jquery.js"],
  function (errors, window) {
    console.log("there have been", window.$("a").length, "nodejs releases!");
  }
);
Answer

I took TheHippo's suggestion and used PhantomJS, I created a JS -

svggen.js:

var page = require('webpage').create(),
    url = 'http://www.example.com/wordcloud.html';

page.open(url, function (status) {
    if (status !== 'success') {
        console.log('Unable to access network');
    } else {
        var svgData = page.evaluate(function(s){
                var serializer = new XMLSerializer();
                var element = document.getElementById("svg1");
                return serializer.serializeToString(element);
        });
        console.log("<?xml version=\"1.0\"?>"+svgData);
    }
    phantom.exit();
});

wordcloud.html:

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="d3.min.js"></script>
<script src="d3.layout.cloud.js"></script>
<script>
  var fill = d3.scale.category20();

  d3.layout.cloud().size([500, 800])
      .words([
        "Hello", "world", "normally", "you", "want", "more", "words",
        "than", "this"].map(function(d) {
        return {text: d, size: 10 + Math.random() * 90};
      }))
      .padding(5)
      .rotate(function() { return ~~(Math.random() * 2) * 90; })
      .font("Impact")
      .fontSize(function(d) { return d.size; })
      .on("end", draw)
      .start();

  function draw(words) {
    d3.select("body").append("svg")
        .attr("width", 500)
        .attr("height", 800)
        .attr("id","svg1")
        .attr("xmlns","http://www.w3.org/2000/svg")
        .attr("xmlns:xlink","http://www.w3.org/1999/xlink")
      .append("g")
        .attr("transform", "translate(150,150)")
      .selectAll("text")
        .data(words)
      .enter().append("text")
        .style("font-size", function(d) { return d.size + "px"; })
        .style("font-family", "Impact")
        .style("fill", function(d, i) { return fill(i); })
        .attr("text-anchor", "middle")
        .attr("transform", function(d) {
          return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
        })
        .text(function(d) { return d.text; });
  }
</script>
</body></html>

Then I run

phantomjs svggen.js > svgFile.svg

The resulting svgFile.svg is a standalone SVG File. For d3cloud check this.

Answer

You don't need phantomjs if you are just rendering a svg with d3.js

You could run d3 directly in node.js

global.d3 = d3 = require "d3"
cloud     = require 'd3.layout.cloud'
fs = require 'fs'

draw = (words) ->
  d3.select("body").append("svg").attr("width", 500).attr("height", 800).attr("id", "svg1").attr("xmlns", "http://www.w3.org/2000/svg").attr("xmlns:xlink", "http://www.w3.org/1999/xlink").append("g").attr("transform", "translate(150,150)").selectAll("text").data(words).enter().append("text").style("font-size", (d) ->
    d.size + "px"
  ).style("font-family", "Impact").style("fill", (d, i) ->
    fill i
  ).attr("text-anchor", "middle").attr("transform", (d) ->
    "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")"
  ).text (d) ->
    d.text

fill = d3.scale.category20()
cloud().size([500, 800]).words(["Hello", "world", "normally", "you", "want", "more", "words", "than", "this"].map((d) ->
  text: d
  size: 10 + Math.random() * 90
)).padding(5).rotate(->
  ~~(Math.random() * 2) * 90
).font("Impact").fontSize((d) ->
  d.size
).on("end", draw).start()

svg = d3.select('body').node().innerHTML

console.log svg
fs.writeFile 'svgFile.svg', svg
Answer

You can create a DOM without a document by creating a document fragment. Once you have that, you can append a root <svg> element to it, then pass that node to d3.select(node). From there on, the rest of d3 methods should work as normal.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.