The question is published on by Tutorial Guruji team.
Attached is my minimum working example in JS Fiddle. I am able to only show the initial data, but when I change the slider, the data does not update. Where am I going wrong in my code here? Apologies for the poor structure – I am still a beginner in D3.
https://jsfiddle.net/pv02z8em/1/
chartGroup .selectAll('.line-series') .data(data, d=> d.x) .join( enter => { enter.append('path') .attr('class', d => `line-series x_${d.x}`) .attr("d", drawLine(data)) .style('stroke', 'dodgerblue') .style('stroke-width', 2) .style('fill', 'none') .style('opacity', 1) }, update => { update.transition().duration(500) }, exit => { exit.remove() } ) }
Answer
You have several issues in that code:
You are not selecting the slider by its ID. It should be
d3.select('#slider-x-range')
;In the listener, you’re not calling
buildLine(data)
;Remove everything inside
buildLine
that isn’t related to the path itself, otherwise you’ll create different SVGs every time the user moves the slider;Your
join
structure is currently appending several paths, one above the other. This is certainly not what you want. It could be just:let path = chartGroup.selectAll('.line-series') .data([data]); path = path.enter() .append('path') .attr('class', "line-series") .attr("d", d => drawLine(d)) .style('stroke', 'dodgerblue') .style('stroke-width', 2) .style('fill', 'none') .style('opacity', 1) .merge(path) path.transition().duration(500).attr("d", d => drawLine(d))
Here is your code with these and other minor changes:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="css/normalize.css"> <link rel="stylesheet" href="css/skeleton.css"> <link rel="stylesheet" href="css/skeleton_override.css"> <style> svg { display: inline-block; position: relative; vertical-align: top; overflow: hidden; } .x-axis, .y-axis { font: 16px sans-serif; } .axis-label { font: 18px sans-serif; } .chart-title { font: 24px sans-serif; } .x-axis .tick:first-of-type text { fill: none; } .body { display: flex; } .chart-group-container { margin: 10px 20px 20px 20px; } .controls-container { display: flex; flex-direction: column; } .controls-header { color: black; padding: 0.5rem 2rem; text-align: left; } .controls-body { overflow: auto; font-size: 0.8em; cursor: default; } .slidecontainer { text-align: left; margin: 10px; font-family: sans-serif; font-size: 14px; } #slider-x-range { vertical-align: bottom; } </style> <title>Document</title> </head> <body> <div class="chart-group-container"> <div class="row"> <div class="six columns"> <div class="chart-container"> <div class="viz"> </div> </div> </div> <div class="six columns"></div> <div class="controls-container"> <div class="controls-header">UI Controls</div> <div class="controls-body"> <div class="slider-label">Adjust x axis</div> <div class="slidecontainer"> <span>10</span> <input type="range" min="10" max="100" value="1" id="slider-x-range"> <span>100</span> </div> </div> </div> </div> <script src="https://d3js.org/d3.v5.min.js"></script> <script> //let sinWave = Math.sin(x) let range = function(start, stop, step) { step = step || 1; let arr = [] for (let i = start; i < stop; i += step) { arr.push(i); } return arr; } let generateSinWave = function(x) { let y = [] x.forEach(function(i) { y.push(Math.sin(i)) }); return y; } const generateData = (n) => { x = range(0, n, 1) y = generateSinWave(x) let labels = ['x', 'y'] let data = [] for (let i = 0; i < x.length; i++) { data.push({ x: x[i], y: y[i] }) } return data; } </script> <script> let margin = { top: 50, right: 30, bottom: 30, left: 100 }, width = 800 - margin.left - margin.right height = 400 - margin.top - margin.bottom; let xScale = d3.scaleLinear() .range([0, width]) let yScale = d3.scaleLinear() .range([height, 0]) .nice() let drawLine = d3.line() .x(d => xScale(d.x)) .y(d => yScale(d.y)) .curve(d3.curveBasis); let svg = d3.select('.viz') .append('svg') .attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`) .attr("preserveAspectRatio", "xMinYMin meet") //.attr('width', `${width + margin.left + margin.right}px`) //.attr('height', `${height + margin.top + margin.bottom}px`) //.classed("svg-content", true); .append('g') .attr('class', 'line-chart-container') .attr('transform', `translate(${margin.left}, ${margin.top})`); /* d3.select(".line-chart-container") .attr("style", "outline: thin solid black;") .attr("margin-right", "102px") */ const chartGroup = svg.append('g').attr('class', 'line-chart') // Draw x axis const xAxis = d3.axisBottom(xScale).tickSizeOuter(0); const xAxisDraw = svg .append('g') .attr('class', 'x-axis') //.style('font', '14px sans-serif') .attr('transform', `translate(0, ${height / 2})`) .call(xAxis); const yAxis = d3 .axisLeft(yScale) .ticks(10) //.tickSizeInner(-width); const yAxisDraw = svg .append('g') .attr('class', 'y-axis') .call(yAxis); // x axis label svg.append('text') .attr('class', 'axis-label') .attr('text-anchor', 'end') .text('X axis') .attr('x', width) .attr('y', height - margin.bottom + 50) // y axis label svg.append('text') .attr('class', 'axis-label') .attr('text-anchor', 'end') .attr('transform', 'rotate(-90)') .attr('x', margin.top + 50 - (height / 2)) .attr('y', margin.left - 160) .text('Y axis') // Draw Header const header = svg .append('g') .attr('class', 'chart-title') .attr('transform', `translate(${width / 2 - 75}, ${margin.top - 75})`) .append('text') header.append('tspan').text('Sine wave') function buildLine(data) { xScale.domain([d3.min(data, d => d.x), d3.max(data, d => d.x)]) yScale.domain([d3.min(data, d => d.y), d3.max(data, d => d.y)]) let path = chartGroup .selectAll('.line-series') .data([data]); path = path.enter().append('path') .attr('class', "line-series") .attr("d", d => drawLine(d)) .style('stroke', 'dodgerblue') .style('stroke-width', 2) .style('fill', 'none') .style('opacity', 1) .merge(path) path.transition().duration(500).attr("d", d => drawLine(d)) } let xRangeSlider = document.getElementById('slider-x-range'); xRangeSlider.min = 10; xRangeSlider.max = 100; let data = generateData(xRangeSlider.value) buildLine(data) d3.select('#slider-x-range') .on("change", d => { data = generateData(xRangeSlider.value) buildLine(data) }); </script> </body> </html>
As you’ll find out, the transition is not what you probably expect: that’s an unrelated issue, the interpolation of the d
property string (you can find more info here).