Public
Edited
Dec 6
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
table = () => {
const wrapper = d3.create("div")
.attr("class", "table-survey");

wrapper.append("div")
.attr("class", "table-title")
.text("People who say they would prefer to live in a community with...")

const legend = wrapper.append("div")
.attr("class", "legend");

const legendItem = legend.selectAll(".legend-item")
.data(["smaller_walking", "larger_driving"].map(d => legendLookup[d]))
.join("div")
.attr("class", "legend-item");

legendItem.append("div")
.attr("class", "legend-swatch")
.style("background", d => d.background)

legendItem.append("div")
.attr("class", "legend-label")
.text(d => d.text);

wrapper.append("style").html(css);
const category = wrapper.selectAll(".category")
.data(categories)
.join("div")
.attr("class", "category")
.style("position", "relative");

const axis = category.append("svg")
.style("position", "absolute")
.style("overflow", "visible")
.style("left", `${leftColWidth}px`)
.style("top", (_, i) => i ? "29px" : "0px")
.attr("width", chartWidth)
.attr("height", overlayHeight);

axis.filter((_, i) => !i).append("text")
.attr("fill", "#666666")
.attr("x", chartWidth / 2)
.attr("y", -3)
.attr("text-anchor", "middle")
.attr("font-size", 14)
.text("50%");

axis.append("line")
.attr("x1", chartWidth / 2)
.attr("x2", chartWidth / 2)
.attr("stroke", "#666666")
.attr("y2", overlayHeight)

category
.filter((_, i) => i)
.append("div")
.attr("class", "category-name")
.text(d => `${categoryLookup[d[0]] || d[0]}`);

const table = category.append("table");
const tbody = table.append("tbody");
const tr = tbody.selectAll("tr")
.data(d => d[1])
.join("tr");
const td = tr.selectAll("td")
.data(d => {
return cols
.map(key => ({
key,
value: d[key] || ["smaller_walking", "larger_driving"]
.map(c => ({
category: d.category,
c,
v: d[c]
}))
}))
})
.join("td")
.attr("class", d => d.key)
.text(d => subcategoryLookup[d.value] || (typeof d.value === "string"? d.value : ""));

const svg = td.filter((_, i) => i).append("svg")
.attr("width", chartWidth)
.attr("height", rowHeight);

svg.append("rect")
.attr("class", "bg")
.attr("width", chartWidth)
.attr("height", rowHeight)

svg.selectAll(".bar")
.data(d => d.value)
.join("rect")
.attr("class", d => `bar ${d.c}`)
.attr("height", rowHeight)
.attr("width", d => d.v / 100 * chartWidth)
.attr("x", (d, i) => i == 0 ? 0 : chartWidth - (d.v / 100 * chartWidth));

svg.selectAll(".value")
.data(d => d.value)
.join("text")
.attr("class", d => `value ${d.c}`)
.attr("dx", (d, i) => 6 * (1 - 2 * i))
.attr("dy", "0.4em")
.attr("text-anchor", (d, i) => i == 0 ? "start" : "end")
.attr("x", (d, i) => i == 0 ? 0 : chartWidth)
.attr("y", rowHeight / 2)
.text(d => `${d.v}${d.category === "total" ? "%" : ""}`);
return wrapper.node();
}
Insert cell
Insert cell
css = `
.table-survey {
font-family: ${franklinLight};
max-width: ${maxWidth}px;

.table-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 16px;
}

.legend {
margin-bottom: 32px;

.legend-item {
display: block;
.legend-label {
display: inline;
font-size: 16px;
}
.legend-swatch {
display: inline-block;
margin-right: 8px;
width: 11px;
height: 11px;
}
}
}


.category-name {
font-size: 16px;
font-weight: bold;
margin-bottom: -12px;
margin-top: 18px;
text-transform: uppercase;
}

table {
font-family: ${franklinLight};
font-size: 16px;
width: 100%;

td {
border-collapse: collapse;
font-weight: normal;
width: ${chartWidth}px;

&.chart {
padding: 0px;
}

svg {
margin-bottom: -3px;
overflow: visible;

rect {
&.bg {
fill: #ededed;
}
&.larger_driving {
fill: #fdb863;
}
&.smaller_walking {
fill: #b2abd2;
}
}
}
}
}
}
`
Insert cell
legendLookup = ({
"smaller_walking": {
text: "Smaller houses within walking distance of schools, stores and restaurants",
background: "#b2abd2",
},
"larger_driving": {
text: "Larger houses, but schools, stores and restaurants are several miles away",
background: "#fdb863",
}
})
Insert cell
subcategoryLookup = ({
"Postgrad": "Postgraduate",
"HS or less": "High school or less",
"Rep/Lean Rep": "Republican",
"Conservative Rep/Lean Rep": "Conservative Rep.",
"Mod/Lib Rep/Lean Rep": "Moderate/Liberal Rep.",
"Dem/Lean Dem": "Democrat",
"Cons/Mod Dem/Lean Dem": "Conservative/Moderate Dem.",
"Liberal Dem/Lean Dem": "Liberal Dem."
})
Insert cell
categoryLookup = ({
"community": "community type",
"politics": "political lean"
})
Insert cell
Insert cell
maxWidth = Math.min(width, 640)
Insert cell
leftColWidth = 135
Insert cell
chartWidth = maxWidth - leftColWidth
Insert cell
overlayHeight = (d) => d[1].length * rowHeight + (2 * (d[1].length - 1))
Insert cell
rowHeight = 24
Insert cell
Insert cell
keys = Object.keys(pew[0])
Insert cell
cols = [keys[1], "chart"]
Insert cell
categories = d3.groups(data, d => d.category)
Insert cell
data = {
const unsorted = survey[1]
.filter(({subcategory}) => !subcategory.includes("Conservative") && !subcategory.includes("Mod") && !subcategory.includes("Liberal"))
.filter(({category}) => !["community", "race"].includes(category))

return [
...unsorted.slice(0, 9),
unsorted[10],
unsorted[9]
]
.map(d => {
delete d.survey_date;
delete d.survey_start;
delete d.survey_end;
return d;
})
}
Insert cell
surveys = d3.groups(pew, d => d.survey_date)
Insert cell
// From Nida Asheer, Senior Communications Manager, Pew
pew = FileAttachment("pew.csv").csv({ typed: true })
Insert cell
Insert cell
import { foreground } from "@climatelab/contrasting-foreground-text@182"
Insert cell
import { franklinLight } from "@climatelab/fonts@46"
Insert cell
import { toc } from "@climatelab/toc@45"
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more