使用3级嵌套数据的D3v4更新模式遇到麻烦

时间:2019-06-28 17:06:13

标签: d3.js merge nested updates

我将数据嵌套到3个级别中,需要动态更新它们。更重要的是,由于我需要一些悬停行为,因此中级元素需要实际显示在低级元素的TOP上,因此我在输入/更新/退出/合并模式方面遇到麻烦应该看起来像。 (不需要为高级显示任何元素。)

我现在拥有的代码成功更新了数据,但根本没有渲染矩形,而是给了我一个错误Uncaught TypeError: this.setAttribute is not a function

请问如何解决此问题?

这是更新前的样子:

这是更新后的样子:

Here's a CodePen with the code Below

```
let width = 0.9 * window.innerWidth,
 height = 0.9 * window.innerHeight,
 colors = ['darkviolet', 'steelblue', 'coral', 'Turquoise', 'firebrick', 'mediumslateblue', 'palevioletred', 'green', 'aqua'];

let data1 = 
 [{"group":"A","segment":"1","item":"1"},
  {"group":"A","segment":"1","item":"2"},
  {"group":"A","segment":"1","item":"3"},
  {"group":"A","segment":"2","item":"4"},
  {"group":"A","segment":"2","item":"5"},
  {"group":"A","segment":"2","item":"6"},
  {"group":"A","segment":"3","item":"7"},
  {"group":"A","segment":"3","item":"8"},
  {"group":"A","segment":"3","item":"9"},
  {"group":"B","segment":"4","item":"1"},
  {"group":"B","segment":"4","item":"2"},
  {"group":"B","segment":"4","item":"3"},
  {"group":"B","segment":"5","item":"4"},
  {"group":"B","segment":"5","item":"5"},
  {"group":"B","segment":"5","item":"6"},
  {"group":"B","segment":"6","item":"7"},
  {"group":"B","segment":"6","item":"8"},
  {"group":"B","segment":"6","item":"9"},
  {"group":"C","segment":"7","item":"1"},
  {"group":"C","segment":"7","item":"2"},
  {"group":"C","segment":"7","item":"3"},
  {"group":"C","segment":"8","item":"4"},
  {"group":"C","segment":"8","item":"5"},
  {"group":"C","segment":"8","item":"6"},
  {"group":"C","segment":"9","item":"7"},
  {"group":"C","segment":"9","item":"8"},
  {"group":"C","segment":"9","item":"9"}],
 
data2 = 
 [{"group":"A","segment":"1","item":"1"},
  {"group":"A","segment":"8","item":"2"},
  {"group":"A","segment":"9","item":"3"},
  {"group":"A","segment":"2","item":"4"},
  {"group":"A","segment":"2","item":"5"},
  {"group":"A","segment":"2","item":"6"},
  {"group":"A","segment":"5","item":"7"},
  {"group":"A","segment":"3","item":"8"},
  {"group":"A","segment":"3","item":"9"},
  {"group":"B","segment":"4","item":"1"},
  {"group":"B","segment":"4","item":"2"},
  {"group":"B","segment":"7","item":"3"},
  {"group":"B","segment":"5","item":"4"},
  {"group":"B","segment":"5","item":"5"},
  {"group":"B","segment":"5","item":"6"},
  {"group":"B","segment":"5","item":"7"},
  {"group":"B","segment":"6","item":"8"},
  {"group":"B","segment":"6","item":"9"},
  {"group":"C","segment":"7","item":"1"},
  {"group":"C","segment":"7","item":"2"},
  {"group":"C","segment":"3","item":"3"},
  {"group":"C","segment":"8","item":"4"},
  {"group":"C","segment":"8","item":"5"},
  {"group":"C","segment":"8","item":"6"},
  {"group":"C","segment":"9","item":"7"},
  {"group":"C","segment":"6","item":"8"},
  {"group":"C","segment":"1","item":"9"}]; 

let button = d3.select('body')
.append('button')
.attr('type', 'button')
.style('display', 'block')
.text('Update')
.on('click', function() { update(data2) });

var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height)
.append('g');

let color = d3.scaleOrdinal().range(colors);


update(data1);

function getxy(data) {

let grouped = Array.from(d3.group(data, d=> d.group, d=> d.segment), ([key, value]) => ({key, value}));

grouped.forEach(function(s) {
	s.value = Array.from(s.value, ([key, value]) => ({key, value}));
	s.value.forEach(function(d) {
		d.start = d3.min(d.value, function(t) { t.segment = +t.segment; t.item = +t.item; return +t.item });
		d.end = d3.max(d.value, function(t) { return t.item });
		d.key = +d.key;
		d.group = s.key;
	})
})

let x1 = d3.scaleBand()
	.domain([1, 2, 3, 4, 5, 6, 7, 8, 9])
	.range([width*0.05, width])
	.padding(0.0);

let y1 = d3.scaleBand()
	.domain(['A', 'B', 'C'])
	.range([10, height])
	.padding(0.1);

return [x1, y1, grouped];

}

function update(data) {
let xy = getxy(data);
let x = xy[0], y = xy[1], groupedData = xy[2];


let barsAll = svg
	.selectAll('.bars')
	.data(groupedData);

barsAll.exit().remove();

let barsEnter = barsAll
	.enter()
	.append('g')
	.attr('class', 'bars');

barsEnter = barsEnter.merge(barsAll);

let segmentsAll = barsEnter
	.selectAll('.segments')
	.data(function(d) { return d.value });

segmentsAll.exit().remove();
	
let segmentsEnter = segmentsAll.enter();

let bitsAll = segmentsEnter
	.selectAll('.bits')
	.data(function(d) { return d.value });

bitsAll.exit().remove();

let bitsEnter = bitsAll
	.enter()
	.append('circle')
	.attr('class', 'bits')
	.attr('r', width*0.05)
	.attr('stroke', 'none');

bitsEnter = bitsEnter.merge(bitsAll);

bitsEnter
	.attr('cx', function(d) { return x(d.item) })
	.attr('cy', function(d) { return y(d.group) + y.bandwidth()/2 })
	.attr('fill', function(d) { return color(d.segment) });

	
segmentsEnter.append('rect')
	.attr('stroke', 'black')
	.attr('class', 'segments')
	.style('fill-opacity', 0.2);

segmentsEnter = segmentsEnter.merge(segmentsAll);

segmentsEnter
	.attr('fill', function(d) { return color(d.key) })
	.attr('height', y.bandwidth()*0.75)
	.attr('x', function(d) { return x(d.start) - width*0.05 })
	.attr('y', function(d) { return y(d.group) + y.bandwidth()*0.125 })
	.attr('width', function(d) { return x(d.end) - x(d.start) + width*0.1 });
}
```
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-array.v2.min.js"></script>

1 个答案:

答案 0 :(得分:0)

好吧,通过绕过中级段和低级位(而不是顶级条)上的“合并”步骤,我终于可以破解补丁了。 Working pen

仍然开放以寻求他人的帮助,因为我觉得我应该真正掌握整个流程的全部内容-更新,输入,退出,合并-在某个时候。

let width = 0.9 * window.innerWidth,
	 height = 0.9 * window.innerHeight,
	 colors = ['darkviolet', 'steelblue', 'coral', 'Turquoise', 'firebrick', 'mediumslateblue', 'palevioletred', 'green', 'aqua'];


let data1 = 
	 [{"group":"A","segment":"1","item":"1"},
	  {"group":"A","segment":"1","item":"2"},
	  {"group":"A","segment":"1","item":"3"},
	  {"group":"A","segment":"2","item":"4"},
	  {"group":"A","segment":"2","item":"5"},
	  {"group":"A","segment":"2","item":"6"},
	  {"group":"A","segment":"3","item":"7"},
	  {"group":"A","segment":"3","item":"8"},
	  {"group":"A","segment":"3","item":"9"},
	  {"group":"B","segment":"4","item":"1"},
	  {"group":"B","segment":"4","item":"2"},
	  {"group":"B","segment":"4","item":"3"},
	  {"group":"B","segment":"5","item":"4"},
	  {"group":"B","segment":"5","item":"5"},
	  {"group":"B","segment":"5","item":"6"},
	  {"group":"B","segment":"6","item":"7"},
	  {"group":"B","segment":"6","item":"8"},
	  {"group":"B","segment":"6","item":"9"},
	  {"group":"C","segment":"7","item":"1"},
	  {"group":"C","segment":"7","item":"2"},
	  {"group":"C","segment":"7","item":"3"},
	  {"group":"C","segment":"8","item":"4"},
	  {"group":"C","segment":"8","item":"5"},
	  {"group":"C","segment":"8","item":"6"},
	  {"group":"C","segment":"9","item":"7"},
	  {"group":"C","segment":"9","item":"8"},
	  {"group":"C","segment":"9","item":"9"}],
	 
	data2 = 
	 [{"group":"A","segment":"1","item":"1"},
	  {"group":"A","segment":"8","item":"2"},
	  {"group":"A","segment":"9","item":"3"},
	  {"group":"A","segment":"2","item":"4"},
	  {"group":"A","segment":"2","item":"5"},
	  {"group":"A","segment":"2","item":"6"},
	  {"group":"A","segment":"5","item":"7"},
	  {"group":"A","segment":"3","item":"8"},
	  {"group":"A","segment":"3","item":"9"},
	  {"group":"B","segment":"4","item":"1"},
	  {"group":"B","segment":"4","item":"2"},
	  {"group":"B","segment":"7","item":"3"},
	  {"group":"B","segment":"5","item":"4"},
	  {"group":"B","segment":"5","item":"5"},
	  {"group":"B","segment":"5","item":"6"},
	  {"group":"B","segment":"5","item":"7"},
	  {"group":"B","segment":"6","item":"8"},
	  {"group":"B","segment":"6","item":"9"},
	  {"group":"C","segment":"7","item":"1"},
	  {"group":"C","segment":"7","item":"2"},
	  {"group":"C","segment":"3","item":"3"},
	  {"group":"C","segment":"8","item":"4"},
	  {"group":"C","segment":"8","item":"5"},
	  {"group":"C","segment":"8","item":"6"},
	  {"group":"C","segment":"9","item":"7"},
	  {"group":"C","segment":"6","item":"8"},
	  {"group":"C","segment":"1","item":"9"}]; 

let button = d3.select('body')
	.append('button')
	.attr('type', 'button')
	.style('display', 'block')
	.text('Update')
	.on('click', function() { update(data2) });

var svg = d3.select('body').append('svg')
	.attr('width', width)
	.attr('height', height)
	.append('g');

let color = d3.scaleOrdinal().range(colors);

function getxy(data) {
	
	let grouped = Array.from(d3.group(data, d=> d.group, d=> d.segment), ([key, value]) => ({key, value}));
	
	grouped.forEach(function(s) {
		s.value = Array.from(s.value, ([key, value]) => ({key, value}));
		s.value.forEach(function(d) {
			d.start = d3.min(d.value, function(t) { t.segment = +t.segment; t.item = +t.item; return +t.item });
			d.end = d3.max(d.value, function(t) { return t.item });
			d.key = +d.key;
			d.group = s.key;
		})
	})
	
	let x1 = d3.scaleBand()
		.domain([1, 2, 3, 4, 5, 6, 7, 8, 9])
		.range([width*0.05, width])
		.padding(0.0);

	let y1 = d3.scaleBand()
		.domain(['A', 'B', 'C'])
		.range([10, height])
		.padding(0.1);
	
	return [x1, y1, grouped];

}

function update(data) {
	let xy = getxy(data);
	let x = xy[0], y = xy[1], groupedData = xy[2];

	// update
	let barsAll = svg
		.selectAll('.bars')
		.data(groupedData);
	
	// exit
	barsAll.exit().remove();
	
	// enter
	let barsEnter = barsAll
		.enter();
	
	barsEnter = barsEnter.merge(barsAll).append('g');
	
	barsEnter.selectAll('.segments').remove();
	
	d3.selectAll('.segments').remove();
	
	let segmentsAll = barsEnter
		.selectAll('.segments')
		.data(function(d) { return d.value });
	
	segmentsAll.exit().remove();
		
	let segmentsEnter = segmentsAll
		.enter();
	
	let bitsAll = segmentsEnter
		.selectAll('.bits')
		.data(function(d) { return d.value });
	
	bitsAll.exit().remove();
	
	bitsAll
		.enter()
		.append('circle')
		.attr('class', 'bits')
		.attr('r', width*0.05)
		.attr('stroke', 'none')
		.attr('cx', function(d) { return x(d.item) })
		.attr('cy', function(d) { return y(d.group) + y.bandwidth()/2 })
		.attr('fill', function(d) { return color(d.segment) });
	
//	bitsEnter = bitsEnter.merge(bitsAll);
	
	bitsAll
		.attr('cx', function(d) { return x(d.item) })
		.attr('cy', function(d) { return y(d.group) + y.bandwidth()/2 })
		.attr('fill', function(d) { return color(d.segment) });

	segmentsEnter
		.append('rect')
		.attr('class', 'segments')
		.attr('stroke', 'black')
		.style('fill-opacity', 0.2)
		.attr('fill', function(d) { return color(d.key) })
		.attr('height', function() { return y.bandwidth()*0.75 })
		.attr('x', function(d) { return x(d.start) - width*0.05 })
		.attr('y', function(d) { return y(d.group) + y.bandwidth()*0.125 })
		.attr('width', function(d) { return x(d.end) - x(d.start) + width*0.1 });
	
	segmentsAll
		.attr('fill', function(d) { return color(d.key) })
		.attr('height', function() { return y.bandwidth()*0.75 })
		.attr('x', function(d) { return x(d.start) - width*0.05 })
		.attr('y', function(d) { return y(d.group) + y.bandwidth()*0.125 })
		.attr('width', function(d) { return x(d.end) - x(d.start) + width*0.1 });
	
	//segmentsAll = segmentsEnter.merge(segmentsAll);

	// segmentsEnter
	// 	.attr('fill', function(d) { return color(d.key) })
	// 	.attr('height', function() { return y.bandwidth()*0.75 })
	// 	.attr('x', function(d) { return x(d.start) - width*0.05 })
	// 	.attr('y', function(d) { return y(d.group) + y.bandwidth()*0.125 })
	// 	.attr('width', function(d) { return x(d.end) - x(d.start) + width*0.1 });
		
}
update(data1);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>
<script src="https://d3js.org/d3-array.v2.min.js"></script>