代码之家  ›  专栏  ›  技术社区  ›  Jacob Lockett

将Dynamic Attr()添加到d3.tip

  •  1
  • Jacob Lockett  · 技术社区  · 6 年前

    我想添加一个 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 让tip传递数据,但这也不起作用。

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

    有人有什么想法吗?

    1 回复  |  直到 6 年前
        1
  •  1
  •   i alarmed alien    6 年前

    d3-tip 是一个非常独立的实体,所以如果你想这样做,你基本上要破解它;没有一个很好的方法可以通过官方API来实现。在 html 函数,您可以操纵 tip DOM元素并使用当前数据项向其添加属性:

                .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>