Published
Edited
Aug 12, 2020
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
updateBarsOld = (svg, data) => {
const rectWidth = 50
// ✨ OUR CODE HERE
const rect = d3.select(svg).selectAll('rect')
.data(data, d => d)
// exit
rect.exit().remove()
const enter = rect.enter().append('rect') // enter: attr's that don't change with data
.attr('width', rectWidth)
.attr('fill', 'pink')
.attr('stroke', 'plum')
.attr('stroke-width', 2)

enter.merge(rect) // enter + update selections => merge: attr's that change with data
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => 100 - d)
.attr('height', d => d)
}
Insert cell
Insert cell
updateBarsNew = (svg, data) => {
const rectWidth = 50

// ✨ OUR CODE HERE
d3.select(svg).selectAll('rect')
.data(data, d => d)
.join('rect') // takes care of enter & exit in one
.attr('width', rectWidth)
.attr('fill', 'pink')
.attr('stroke', 'plum')
.attr('stroke-width', 2)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => 100 - d)
.attr('height', d => d)
}
Insert cell
Insert cell
{
const oldSVG = html`<svg height=100 style='overflow: visible' />`
const newSVG = html`<svg height=100 style='overflow: visible' />`
const code = html`<code />`
const button = html`<button>new data!</button>`
d3.select(button).on('click', d => {
// randomly generate an array of data
const data = _.times(_.random(3, 8), i => _.random(0, 100))

updateBarsOld(oldSVG, data)
updateBarsNew(newSVG, data)
// update div with new data array:
d3.select(code).text(JSON.stringify(data).replace(/\,/g, ', '))
})
return html`
<h4>old way:</h4>
${oldSVG}
<h4>new way:</h4>
${newSVG}
<p>
${button} ${code}
</p>
`
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const rectWidth = 50
const svgHeight = 100
const svg = html`<svg height=${svgHeight} style='overflow: visible' />`
const code = html`<code />`
const button = html`<button>new data!</button>`
function updateBars() {
const t = d3.transition().duration(1000)
// randomly generate an array of data
const data = _.times(_.random(3, 8), i => _.random(0, 100))
// ✨ YOUR CODE HERE
let rect = d3.select(svg).selectAll('rect')
.data(data, d => d)
.join(
enter => {
// return so it can be joined with update selection
return enter.append('rect')
.attr('fill', 'pink')
.attr('stroke', 'plum')
.attr('stroke-width', 2)
.attr('x', (d, i) => i * rectWidth)
.attr('y', svgHeight)
.attr('height', 0)
// set attributes etc. on only enter selection
},
update => update,
exit => {
// do something with exit selection
return exit.transition(t)
.attr('y', svgHeight)
.attr('height', 0)
}
) // enter + update selection
.attr('width', rectWidth)
.transition(t)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => 100 - d)
.attr('height', d => d)

// update div with new data array:
d3.select(code).text(JSON.stringify(data).replace(/\,/g, ', '))
}
updateBars()
d3.select(button).on('click', updateBars)
return html`
${svg}
<p>
${button} ${code}
</p>
`
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
// this line is a little bit of Observable magic to make sure that every time
// our filter cells update, we use the existing SVG element instead of trashing it and creating a new one
// if you're working in your index.html, you'd create your svg element and select it as we did before
const element = this || scrollSVG(html`<svg width=${width} height=${svgHeight}></svg>`)
// create transition animation
const t = d3.transition().duration(1000)
const svg = d3.select(element).select('svg')
// ✨ YOUR CODE HERE
const g = svg.selectAll('g')
.data(filteredFlowers, d => d.title)
.join(
enter => {
const g = enter.append('g')
.attr('opacity', 0)
.attr('transform', d => `translate(${d.translate})`)
g.selectAll('path')
.data(d=> _.times(d.numPetals, i => {
return { rotate: i * 360 / d.numPetals, ...d }
})).join('path') // enter + update + exit
.attr('transform', d => `rotate(${d.rotate})scale(${d.scale})`)
.attr('d', d => d.path)
.attr('fill', d => d.color)
.attr('stroke', d => d.color)
.attr('fill-opacity', 0.5)
.attr('stroke-width', 2)
g.append('text')
.attr('text-anchor', 'middle')
.attr('dy', '.35em')
.style('font-size', '.7em')
.text(d => _.truncate(d.title, { length: 20 }))
return g
},
update => update,
exit => {
exit.transition(t)
.attr('opacity', 0)
.remove()
}
).transition(t)
.attr('opacity', 1)
.attr('transform', d => `translate(${d.translate})`)
return element
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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