Public
Edited
Mar 6, 2023
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()

// enter
const enter = rect.enter().append('rect')
.attr('width', rectWidth)
.attr('fill', 'pink')
.attr('stroke', 'black')

// enter + update
enter.merge(rect)
.attr('height', d => d)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => 100 - 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')
.attr('width', rectWidth)
.attr('fill', 'pink')
.attr('stroke', 'black')
.attr('height', d => d)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => 100 - 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() {
// select svg so that transition can be localized within selection
const t = d3.select(svg).transition().duration(1000)
// randomly generate an array of data
const data = _.times(_.random(3, 8), i => _.random(0, 100))
// ✨ YOUR CODE HERE
d3.select(svg).selectAll('rect')
.data(data, d => d)
.join(
enter => enter.append('rect')
.attr('height', 0)
.attr('x', (d, i) => i * rectWidth)
.attr('y', svgHeight)
.call(enter => enter.transition(t)
.attr('height', d => d)
.attr('y', d => svgHeight - d)),
update => update.transition(t)
.attr('x', (d, i) => i * rectWidth),
exit => exit.transition(t)
.attr('height', 0)
.attr('y', svgHeight)
.remove()
)
.attr('width', rectWidth)
.attr('fill', 'pink')
.attr('stroke', 'black')
// 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
filtered = _.filter(movies, d => {
// either the genre is in the filtered genres
return (_.includes(filteredGenres, d.genres[0]) ||
// OR the movie's genre isn't one of the top genres, and "Other" is toggled
(_.includes(filteredGenres, 'Other') && !_.includes(topGenres, d.genres[0]))) &&
// AND movie's pg rating is in filtered pg
_.includes(filteredPG, d.rated)
})
Insert cell
filteredFlowers = calculateData(filtered)
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>`)
const svg = d3.select(element).select('svg')
// create transition animation localized to svg selection
const t = svg.transition().duration(1000)
// ✨ YOUR CODE HERE

const g = svg.selectAll('g')
.data(filteredFlowers, d => d.title)
.join(
enter => {
const flower = enter.append('g')

flower
.attr('transform', d => `translate(${d.translate})`)
.attr('opacity', 0)
.transition(t)
.attr('opacity', 1)

flower.selectAll('path')
.data(d => _.times(d.numPetals, i => ({rotate: i * (360 / d.numPetals), ...d})))
.join('path')
.attr('transform', d => `rotate(${d.rotate}) scale(${d.scale})`)
.attr('d', d => d.path)
.attr('fill', d => d.color)
.attr('fill-opacity', 0.5)
.attr('stroke-width', 2)
.attr('stroke', d => d.color)

flower.append('text')
.text(d => _.truncate(d.title, {length: 18}))
.style('font-size', '.7em')
.style('font-style', 'italic')
.attr('text-anchor', 'middle')
.attr('dy', '0.35em')

return flower
},
update => {
return update.transition(t)
.attr('transform', d => `translate(${d.translate})`)
},
exit => {
return exit
.transition(t)
.attr('opacity', 0)
.remove()
}
)
return element
}
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