Search code examples
javascriptd3.jssvgobservablehq

How to run code and get required output on site "observablehq"


I am new d3.js .I am trying to run code at this link(observablehq).For running this code, I placed that code in index.js file.

d3 = require("d3@5")
height = 500
margin = ({top: 10, right: 10, bottom: 20, left: 40})
data = Object.assign(d3.csvParse(await FileAttachment("data.csv").text(), d3.autoType), {y: "Population"})
groupKey = data.columns[0]
keys = data.columns.slice(1)
x0 = d3.scaleBand()
    .domain(data.map(d => d[groupKey]))
    .rangeRound([margin.left, width - margin.right])
    .paddingInner(0.1)
x1 = d3.scaleBand()
    .domain(keys)
    .rangeRound([0, x0.bandwidth()])
    .padding(0.05)
y = d3.scaleLinear()
    .domain([0, d3.max(data, d => d3.max(keys, key => d[key]))]).nice()
    .rangeRound([height - margin.bottom, margin.top])
color = d3.scaleOrdinal()
    .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"])
xAxis = g => g
    .attr("transform", `translate(0,${height - margin.bottom})`)
    .call(d3.axisBottom(x0).tickSizeOuter(0))
    .call(g => g.select(".domain").remove())
yAxis = g => g
    .attr("transform", `translate(${margin.left},0)`)
    .call(d3.axisLeft(y).ticks(null, "s"))
    .call(g => g.select(".domain").remove())
    .call(g => g.select(".tick:last-of-type text").clone()
        .attr("x", 3)
        .attr("text-anchor", "start")
        .attr("font-weight", "bold")
        .text(data.y))
legend = svg => {
  const g = svg
      .attr("transform", `translate(${width},0)`)
      .attr("text-anchor", "end")
      .attr("font-family", "sans-serif")
      .attr("font-size", 10)
    .selectAll("g")
    .data(color.domain().slice().reverse())
    .join("g")
      .attr("transform", (d, i) => `translate(0,${i * 20})`);

  g.append("rect")
      .attr("x", -19)
      .attr("width", 19)
      .attr("height", 19)
      .attr("fill", color);

  g.append("text")
      .attr("x", -24)
      .attr("y", 9.5)
      .attr("dy", "0.35em")
      .text(d => d);
}
chart = {
    const svg = d3.select(DOM.svg(width, height));

    svg.append("g")
        .selectAll("g")
        .data(data)
        .join("g")
        .attr("transform", d => `translate(${x0(d[groupKey])},0)`)
        .selectAll("rect")
        .data(d => keys.map(key => ({key, value: d[key]})))
        .join("rect")
        .attr("x", d => x1(d.key))
        .attr("y", d => y(d.value))
        .attr("width", x1.bandwidth())
        .attr("height", d => y(0) - y(d.value))
        .attr("fill", d => color(d.key));

    svg.append("g")
        .call(xAxis);

    svg.append("g")
        .call(yAxis);

    svg.append("g")
        .call(legend);

    return svg.node();
}

After that i called index.js file from html script

<!DOCTYPE html>
<html>
<head>
    <title>Visualization 2 (Multiline graph)</title>
    <link rel="stylesheet" href="styles.css">
    <script src="https://unpkg.com/d3@5.6.0/dist/d3.min.js"></script>
</head>
<body>
<svg width="960" height="500"></svg>
<script src="index.js"></script>
</body>
</html>

I created the data.csv file from array

State,Under 5 Years,5 to 13 Years,14 to 17 Years,18 to 24 Years,25 to 44 Years,45 to 64 Years,65 Years and Over
CA,2704659,4499890,2159981,3853788,10604510,8819342,4114496
TX,2027307,3277946,1420518,2454721,7017731,5656528,2472223
NY,1208495,2141490,1058031,1999120,5355235,5120254,2607672
FL,1140516,1938695, 925060,1607297,4782119,4746856,3187797
IL,894368,1558919,725973,1311479,3596343,3239173,1575308
PA,737462,1345341,679201,1203944,3157759,3414001,1910571

This code is not giving the required output that is this graph in vizhub. enter image description here How to get the required output. Here is vizhub link to code

Update: Code is giving me following error in vizhub

SyntaxError: Unexpected keyword 'const' (57:4)
/index.js (line 57)
53 :       .attr("dy", "0.35em")
54 :       .text(d => d);
55 : }
56 : chart = {
57 :     const svg = d3.select(DOM.svg(width, height));
         ^ 

Solution

  • The first thing to notice is that the javascript running on observablehq is a different flavor of js. It needs a special library runtime to run on your browser. Thus, if you copy and paste the code to a .js .html file it won't work right away.

    Observable's Not Javascript

    If you're learning d3 and enjoys Observable environment, you could easily download/embed your code on a page using the runtime.

    Downloading and Embedding Notebooks

    For example, fetching the notebook code from observable and the runtime from jsdelivr. Remember, you could download the sources and host it from your own servers. The code below renders cell chart, the runtime makes sure all the other relevant cells are executed in order.

    <div id="observablehq-758e3cb7"></div>
    <script type="module">
    import {Runtime, Inspector} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js";
    import define from "https://api.observablehq.com/@d3/grouped-bar-chart.js?v=3";
    const inspect = Inspector.into("#observablehq-758e3cb7");
    (new Runtime).module(define, name => name === "chart" ? inspect() : undefined);
    </script>

    Using same approach but exposing the notebook source code, here you can see how your code is abstracted inside the function export default function notebook(runtime, observer) then the runtime is responsible to run it, last two lines

        const runtime = new Runtime();
        const main = runtime.module(notebook, Inspector.into(document.body));
    

    <script type="module">
    
    import {Runtime, Inspector} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js";
    
    // https://observablehq.com/@d3/grouped-bar-chart@81
    export default function notebook(runtime, observer) {
      const main = runtime.module();
      const fileAttachments = new Map([["data.csv","https://static.observableusercontent.com/files/72eda648679b19ee477d3b70a598c977219672ab0c809bd42a0e15fb322894dc78dead25bd50ad1613de459d161dc6278b3add8d5f251547b393b10c4cf00a05"]]);
      main.builtin("FileAttachment", runtime.fileAttachments(name => fileAttachments.get(name)));
      main.variable(observer()).define(["md"], function(md){return(
    md`# Grouped Bar Chart`
    )});
      main.variable(observer("chart")).define("chart", ["d3","DOM","width","height","data","x0","groupKey","keys","x1","y","color","xAxis","yAxis","legend"], function(d3,DOM,width,height,data,x0,groupKey,keys,x1,y,color,xAxis,yAxis,legend)
    {
      const svg = d3.select(DOM.svg(width, height));
    
      svg.append("g")
        .selectAll("g")
        .data(data)
        .join("g")
          .attr("transform", d => `translate(${x0(d[groupKey])},0)`)
        .selectAll("rect")
        .data(d => keys.map(key => ({key, value: d[key]})))
        .join("rect")
          .attr("x", d => x1(d.key))
          .attr("y", d => y(d.value))
          .attr("width", x1.bandwidth())
          .attr("height", d => y(0) - y(d.value))
          .attr("fill", d => color(d.key));
    
      svg.append("g")
          .call(xAxis);
    
      svg.append("g")
          .call(yAxis);
    
      svg.append("g")
          .call(legend);
    
      return svg.node();
    }
    );
      main.variable(observer("legend")).define("legend", ["width","color"], function(width,color){return(
    svg => {
      const g = svg
          .attr("transform", `translate(${width},0)`)
          .attr("text-anchor", "end")
          .attr("font-family", "sans-serif")
          .attr("font-size", 10)
        .selectAll("g")
        .data(color.domain().slice().reverse())
        .join("g")
          .attr("transform", (d, i) => `translate(0,${i * 20})`);
    
      g.append("rect")
          .attr("x", -19)
          .attr("width", 19)
          .attr("height", 19)
          .attr("fill", color);
    
      g.append("text")
          .attr("x", -24)
          .attr("y", 9.5)
          .attr("dy", "0.35em")
          .text(d => d);
    }
    )});
      main.variable(observer("x0")).define("x0", ["d3","data","groupKey","margin","width"], function(d3,data,groupKey,margin,width){return(
    d3.scaleBand()
        .domain(data.map(d => d[groupKey]))
        .rangeRound([margin.left, width - margin.right])
        .paddingInner(0.1)
    )});
      main.variable(observer("x1")).define("x1", ["d3","keys","x0"], function(d3,keys,x0){return(
    d3.scaleBand()
        .domain(keys)
        .rangeRound([0, x0.bandwidth()])
        .padding(0.05)
    )});
      main.variable(observer("y")).define("y", ["d3","data","keys","height","margin"], function(d3,data,keys,height,margin){return(
    d3.scaleLinear()
        .domain([0, d3.max(data, d => d3.max(keys, key => d[key]))]).nice()
        .rangeRound([height - margin.bottom, margin.top])
    )});
      main.variable(observer("color")).define("color", ["d3"], function(d3){return(
    d3.scaleOrdinal()
        .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"])
    )});
      main.variable(observer("xAxis")).define("xAxis", ["height","margin","d3","x0"], function(height,margin,d3,x0){return(
    g => g
        .attr("transform", `translate(0,${height - margin.bottom})`)
        .call(d3.axisBottom(x0).tickSizeOuter(0))
        .call(g => g.select(".domain").remove())
    )});
      main.variable(observer("yAxis")).define("yAxis", ["margin","d3","y","data"], function(margin,d3,y,data){return(
    g => g
        .attr("transform", `translate(${margin.left},0)`)
        .call(d3.axisLeft(y).ticks(null, "s"))
        .call(g => g.select(".domain").remove())
        .call(g => g.select(".tick:last-of-type text").clone()
            .attr("x", 3)
            .attr("text-anchor", "start")
            .attr("font-weight", "bold")
            .text(data.y))
    )});
      main.variable(observer("data")).define("data", ["d3","FileAttachment"], async function(d3,FileAttachment){return(
    Object.assign(d3.csvParse(await FileAttachment("data.csv").text(), d3.autoType), {y: "Population"})
    )});
      main.variable(observer("groupKey")).define("groupKey", ["data"], function(data){return(
    data.columns[0]
    )});
      main.variable(observer("keys")).define("keys", ["data"], function(data){return(
    data.columns.slice(1)
    )});
      main.variable(observer("margin")).define("margin", function(){return(
    {top: 10, right: 10, bottom: 20, left: 40}
    )});
      main.variable(observer("height")).define("height", function(){return(
    500
    )});
      main.variable(observer("d3")).define("d3", ["require"], function(require){return(
    require("d3@5")
    )});
      return main;
    }
    
    const runtime = new Runtime();
    const main = runtime.module(notebook, Inspector.into(document.body));
    
    </script>

    Now if you want to manually convert the code running on Observable to vanilla js, then you might need to look into other references,

    How to run a D3 example

    D3 example from Observable on my wordpress site

    observable to vanillajs