将动态 Attr() 添加到 d3.tip



问题:

我正在尝试将具有数据集第一个索引的data-data属性添加到我的每个工具提示中,因为它们是由代码生成的,但我一生都无法弄清楚如何基于数据集动态设置它。

例如,我想实现等效于此:

const tip = d3.tip()
.attr('data-date', d => d[0])
//...

下面是一个代码片段:

//need to fix tooltip data-date attr.. don't know how I'll do that yet though
const endpoint =
	"https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json";
d3.json(endpoint).then(data => {
	const gc = d3.select(document.getElementById("graph-container")),
		dataset = data.data,
		canvasWidth = 900,
		canvasHeight = 477,
		padding = 50,
		years = dataset.map(s => s[0].slice(0, 4)),
		xScale = d3
			.scaleLinear()
			.domain([0, dataset.length])
			.range([padding, canvasWidth - padding]),
		yScale = d3
			.scaleLinear()
			.domain([0, d3.max(dataset, d => d[1])])
			.range([padding, canvasHeight - padding]),
		tip = d3 //making a tooltip
			.tip()
			.attr("id", "tooltip")
			.direction("e")
			.offset([0, 25])
			.html(
				d =>
					`${d[0].slice(0, 4)} Q${
						d[0].slice(5, 7) === "01"
							? "1"
							: d[0].slice(5, 7) === "04"
								? "2"
								: d[0].slice(5, 7) === "07"
									? "3"
									: d[0].slice(5, 7) === "10" ? "4" : null
					}<br />$${d[1]} Billion`
			),
		xAxis = d3
			.axisBottom(
				d3
					.scaleLinear()
					.domain([d3.min(years), d3.max(years)])
					.range([padding, canvasWidth - padding])
			)
			.tickFormat(d3.format("d")),
		yAxis = d3.axisLeft(
			d3
				.scaleLinear()
				.domain([0, d3.max(dataset, d => d[1])])
				.range([canvasHeight - padding, padding])
		); //not sure how to make this work without redefining the range. point is, however, it works... so... take that
	gc //making the title element
		.append("div")
		.attr("id", "title")
		.text("National Income and Product Accounts of the United States (NIPA)");
	gc //making the graph
		.append("svg")
		.attr("id", "canvas")
		.attr("width", canvasWidth)
		.attr("height", canvasHeight)
		.call(tip)
		.selectAll("rect")
		.data(dataset)
		.enter()
		.append("rect")
		.attr("class", "bar")
		.style('fill', (d, i) => d[1] <= dataset[i === 0 ? 0 : i - 1][1] ? '#f92727' : '#00cc58')
		.attr("x", (d, i) => xScale(i))
		.attr("y", d => canvasHeight - yScale(d[1]))
		.attr("width", canvasWidth / dataset.length)
		.attr("height", d => yScale(d[1]) - padding)
		.attr("data-date", d => d[0])
		.attr("data-gdp", d => d[1])
		.on("mouseover", tip.show)
		.on("mouseout", tip.hide);
	d3 //add x-axis
		.select(document.getElementById("canvas"))
		.append("g")
		.attr("id", "x-axis")
		.attr("transform", `translate(0, ${canvasHeight - padding})`)
		.call(xAxis);
	d3 //add y-axis
		.select(document.getElementById("canvas"))
		.append("g")
		.attr("id", "y-axis")
		.attr("transform", `translate(${padding}, 0)`)
		.call(yAxis);
});
@import url('https://fonts.googleapis.com/css?family=Lato');
* {
	overflow: hidden;
	font-family: 'Lato';
}
html, body {
	padding: 0;
	margin: 0;
}
#container {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	height: 100vh;
	background-image: linear-gradient(to bottom right, white, #ffffe6);
}
#graph-container {
	box-shadow: 2px 2px 10px 1px gray;
	height: 550px;
	width: 950px;
	border-radius: 5px;
	background-color: white;
	display: flex;
	flex-direction: column;
	align-items: center;
}
#title {
	text-align: center;
	padding-top: 15px;
	font-size: 20px;
	width: 100%;
	padding-bottom: 15px;
	box-shadow: 0 0 5px 0 gray;
}
#canvas {
	background-color: transparent;
	margin-top: 15px;
}
.bar:hover {
	fill: yellow;
}
#tooltip {
	background-color: orange;
	padding: 15px;
	border-radius: 5px;
	min-width: 100px;
	text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.9.1/d3-tip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="container">
	<div id="graph-container"></div>
</div>

我尝试将数据集附加到tip变量并在.attr上传递它,但不幸的是,数据集只能通过.html传递而不会中断。

我尝试移动提示的.call以使数据通过,但这也不起作用。

我必须使用.attr并且无法通过将属性附加到生成的div/span/等来在 html 中伪造它。

有人有什么想法吗?

d3-tip

是一个非常独立的实体,所以如果你想这样做,你基本上将不得不破解它;没有一种好的方法可以通过官方API做到这一点。在html函数期间,您可以操作tipDOM 元素并使用当前数据项向其添加属性:

.html(
d => {
// slightly horrible hack
d3.select('#tooltip').attr('data-date', d[0]);
return `${d[0].slice(0, 4)} Q${
d[0].slice(5, 7) === "01"
? "1"
: d[0].slice(5, 7) === "04"
? "2"
: d[0].slice(5, 7) === "07"
? "3"
: d[0].slice(5, 7) === "10" ? "4" : null
}<br />$${d[1]} Billion`
}),

集成到整个代码中:

//need to fix tooltip data-date attr.. don't know how I'll do that yet though
const endpoint =
	"https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json";
d3.json(endpoint).then(data => {
	const gc = d3.select(document.getElementById("graph-container")),
		dataset = data.data,
		canvasWidth = 900,
		canvasHeight = 477,
		padding = 50,
		years = dataset.map(s => s[0].slice(0, 4)),
		xScale = d3
			.scaleLinear()
			.domain([0, dataset.length])
			.range([padding, canvasWidth - padding]),
		yScale = d3
			.scaleLinear()
			.domain([0, d3.max(dataset, d => d[1])])
			.range([padding, canvasHeight - padding]),
		tip = d3 //making a tooltip
			.tip()
			.attr("id", "tooltip")
			.direction("e")
			.offset([0, 25])
			.html(
				d => {
d3.select('#tooltip').attr('data-date', d[0]);
					return `${d[0].slice(0, 4)} Q${
						d[0].slice(5, 7) === "01"
							? "1"
							: d[0].slice(5, 7) === "04"
								? "2"
								: d[0].slice(5, 7) === "07"
									? "3"
									: d[0].slice(5, 7) === "10" ? "4" : null
					}<br />$${d[1]} Billion`
			}),
		xAxis = d3
			.axisBottom(
				d3
					.scaleLinear()
					.domain([d3.min(years), d3.max(years)])
					.range([padding, canvasWidth - padding])
			)
			.tickFormat(d3.format("d")),
		yAxis = d3.axisLeft(
			d3
				.scaleLinear()
				.domain([0, d3.max(dataset, d => d[1])])
				.range([canvasHeight - padding, padding])
		); //not sure how to make this work without redefining the range. point is, however, it works... so... take that
	gc //making the title element
		.append("div")
		.attr("id", "title")
		.text("National Income and Product Accounts of the United States (NIPA)");
	gc //making the graph
		.append("svg")
		.attr("id", "canvas")
		.attr("width", canvasWidth)
		.attr("height", canvasHeight)
		.call(tip)
		.selectAll("rect")
		.data(dataset)
		.enter()
		.append("rect")
		.attr("class", "bar")
		.style('fill', (d, i) => d[1] <= dataset[i === 0 ? 0 : i - 1][1] ? '#f92727' : '#00cc58')
		.attr("x", (d, i) => xScale(i))
		.attr("y", d => canvasHeight - yScale(d[1]))
		.attr("width", canvasWidth / dataset.length)
		.attr("height", d => yScale(d[1]) - padding)
		.attr("data-date", d => d[0])
		.attr("data-gdp", d => d[1])
		.on("mouseover", tip.show)
		.on("mouseout", tip.hide);
	d3 //add x-axis
		.select(document.getElementById("canvas"))
		.append("g")
		.attr("id", "x-axis")
		.attr("transform", `translate(0, ${canvasHeight - padding})`)
		.call(xAxis);
	d3 //add y-axis
		.select(document.getElementById("canvas"))
		.append("g")
		.attr("id", "y-axis")
		.attr("transform", `translate(${padding}, 0)`)
		.call(yAxis);
});
@import url('https://fonts.googleapis.com/css?family=Lato');
* {
	overflow: hidden;
	font-family: 'Lato';
}
html, body {
	padding: 0;
	margin: 0;
}
#container {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	height: 100vh;
	background-image: linear-gradient(to bottom right, white, #ffffe6);
}
#graph-container {
	box-shadow: 2px 2px 10px 1px gray;
	height: 550px;
	width: 950px;
	border-radius: 5px;
	background-color: white;
	display: flex;
	flex-direction: column;
	align-items: center;
}
#title {
	text-align: center;
	padding-top: 15px;
	font-size: 20px;
	width: 100%;
	padding-bottom: 15px;
	box-shadow: 0 0 5px 0 gray;
}
#canvas {
	background-color: transparent;
	margin-top: 15px;
}
.bar:hover {
	fill: yellow;
}
#tooltip {
	background-color: orange;
	padding: 15px;
	border-radius: 5px;
	min-width: 100px;
	text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.9.1/d3-tip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="container">
	<div id="graph-container"></div>
</div>

最新更新