The main source of D3js solutions is observableHq.com
, but seems impossible (?) to reuse algorithms by copy/paste... Is it? Even checking tutorials like this, there are no simple way (with less plugins or programmer's time-consumtion!) to check and reuse.
Example: I need a fresh 2020 D3js v5 algorithm for indented-tree visualization, and there are a good solution: observableHq.com/@d3/indented-tree.
The download is not useful because is based on complex Runtime class...
But, seems a simple chart-builder algorithm,
chart = { // the indented-tree algorithm
const nodes = root.descendants();
const svg = d3.create("svg")// ...
// ...
return svg.node();
}
Can I, by simple human step-by-step, convert it in a simple HTML, with no complex adaptations, that starts with <script src="https://d3js.org/d3.v5.min.js"></script>
and ends with no Runtime class use?
Imagining my step-by-step for the cited indented-tree algorithm, that I can't finesh and need your help:
Suppose to start with a clean HTML5 template. For example:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>Indented Tree</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
function onLOAD() {
console.log("Hello onLoad!")
// all copy-paste and adaptations HERE.
console.log("!Bye!")
} // \onLOAD
</script>
</head>
<body onload="onLOAD()">
<script>
console.log("Hello!")
// global INITIALIZATIONS HERE.
</script>
</body>
</html>
Prepare global variables, seems root
, nodeSize=17
, and width
Prepare data... JSON data is at the ugly ./files/e6537420...
, I moved to project's root with it's real name, flare-2.json
.
Simple and classical D3js way to read JSON data: d3.json("./flare-2.json").then( data=> console.log(data) );
Must test and check no CORS error, etc.
Prepare data as root
variable. All into the data => {}
block to avoid sync problems...
Seems that root
is based in function(d3,data)
{ let i = 0; return d3.hierarchy(data).eachBefore(d => d.index = i++); }
.
Copy-paste chart =
cited above, after root
inicialization with data.
...
On-comments questions, and answers:
@Mehdi - Could you explain what the problem is with including the D3 script tag and using Runtime library in the code?
When the original ObservableHq algorithm is simple, I need another way, a simple way to reuse it, by copy/paste and minimal adaptations.
@Mehdi - Did you read the Downloading and embedding notebooks tutorial?
Yes, no news there: no "human instruction" about how to reuse code... Only "install it" and "install that". No instructions about "copy/paste and minimal adaptations" that I explained above.
(@nobody) - What you need as answer?
As I show above, a simple human-readable step-by-step procedure to convert... Ideally the final result can by tested, a proof that it works at, for example, JSFiddle, with the copy/paste code and some more adaptation lines to show your point.
Observable now has an embed
feature, details in this page.
Here is a step-by-step process to port the linked observable chart into a self-hosted web page, by copy-pasting the code, and without having to use the observable runtime
library.
Starting from an HTML page and a JavaScript file referenced in the HTML page. Assuming a web server is running and configured as suitable.
Download JSON
link from each data
cell's menu.d3-fetch
d3.json("/path/to/data.json").then(function(data) {
console.log(data); // [{"Hello": "world"}, …]
});
Get the content of each cell containing a variable or a function in the notebook, and put then inside the.then
function from previous step. This notebook visualizer tool can be helpful to identify the relevant cells.
Adapt the syntax of the functions just copied as suitable. For example, the following notebook cell:
root = { let i = 0; return d3.hierarchy(data).eachBefore(d => d.index = i++); }
could be transformed to:
function getRoot(){
let i = 0;
return d3.hierarchy(data).eachBefore(d => d.index = i++);
}
root = getRoot()
If needed by some function from the notebook, define a variable width
, and initialize it with the desired value.
adapt the DOM manipulation code in order to append elements to the DOM, rather than relying on the implicit execution by observable runtime.
Demo in the snipped below:
d3.json("https://rawcdn.githack.com/d3/d3-hierarchy/46f9e8bf1a5a55e94c40158c23025f405adf0be5/test/data/flare.json").then(function(data) {
const width = 800
, nodeSize = 17
, format = d3.format(",")
, getRoot = function(){
let i = 0;
return d3.hierarchy(data).eachBefore(d => d.index = i++);
}
, columns = [
{
label: "Size",
value: d => d.value,
format,
x: 280
},
{
label: "Count",
value: d => d.children ? 0 : 1,
format: (value, d) => d.children ? format(value) : "-",
x: 340
}
]
, root = getRoot()
, chart = function() {
const nodes = root.descendants();
const svg = d3.select('#chart')
.attr("viewBox", [-nodeSize / 2, -nodeSize * 3 / 2, width, (nodes.length + 1) * nodeSize])
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.style("overflow", "visible");
const link = svg.append("g")
.attr("fill", "none")
.attr("stroke", "#999")
.selectAll("path")
.data(root.links())
.join("path")
.attr("d", d => `
M${d.source.depth * nodeSize},${d.source.index * nodeSize}
V${d.target.index * nodeSize}
h${nodeSize}
`);
const node = svg.append("g")
.selectAll("g")
.data(nodes)
.join("g")
.attr("transform", d => `translate(0,${d.index * nodeSize})`);
node.append("circle")
.attr("cx", d => d.depth * nodeSize)
.attr("r", 2.5)
.attr("fill", d => d.children ? null : "#999");
node.append("text")
.attr("dy", "0.32em")
.attr("x", d => d.depth * nodeSize + 6)
.text(d => d.data.name);
node.append("title")
.text(d => d.ancestors().reverse().map(d => d.data.name).join("/"));
for (const {label, value, format, x} of columns) {
svg.append("text")
.attr("dy", "0.32em")
.attr("y", -nodeSize)
.attr("x", x)
.attr("text-anchor", "end")
.attr("font-weight", "bold")
.text(label);
node.append("text")
.attr("dy", "0.32em")
.attr("x", x)
.attr("text-anchor", "end")
.attr("fill", d => d.children ? null : "#555")
.data(root.copy().sum(value).descendants())
.text(d => format(d.value, d));
}
}
chart()
}).catch(function(err) {
console.log('error processing data', err)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.8.0/d3.min.js"></script>
<svg id = 'chart'></svg>