我正在构建需要工具提示的D3树图。工具提示是一个div
元素,我通过在树形图的每个叶子上附加的“ mouseover”和“ mouseleave”事件来进行切换。开发者控制台确认元素已添加到DOM,它在div
中具有正确的文本显示,悬停时将“ display”设置为“ block”,“ z-index”为10 ,并且“位置”是“绝对”,但是,我无法使工具提示真正按预期出现。
这里是Codepen的链接。
这是我的实际代码:
HTML:
<script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>
<div>
<h1 id='title'>JSON Data Treemaps</h1>
<h2 id='description'>Top 100 Most Pledged Kickstarter Campaigns</h2>
<svg id="canvas"></svg>
<svg id="legend"></svg>
</div>
CSS:
@import url('https://fonts.googleapis.com/css?family=Noto+Sans');
body {
background-color: ghostwhite;
display: flex;
flex-direction: column;
font-size: 16px;
font-family: 'Noto Sans', sans-serif;
div#tooltip {
position: absolute;
}
div {
display: flex;
flex-direction: column;
h1 {
margin: 4.6rem auto 0;
font-size: 3.2rem;
}
h2 {
margin: 1rem auto 2rem;
font-size: 1.4rem;
}
svg {
margin: auto;
}
svg#legend {
margin: 2rem auto;
}
}
}
JS:
const margin = {
top: 30,
bottom: 100,
left: 30,
right: 30
},
width = 960 - margin.left - margin.right,
height = 570 - margin.top - margin.bottom;
const KICK_STARTER_DATA_URL = 'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/kickstarter-funding-data.json';
const MOVIE_DATA_URL = 'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/movie-data.json';
const VIDEO_GAME_DATA_URL = 'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/video-game-sales-data.json';
// Load all data and assign to variables.
d3.queue()
.defer(d3.json, KICK_STARTER_DATA_URL)
.defer(d3.json, MOVIE_DATA_URL)
.defer(d3.json, VIDEO_GAME_DATA_URL)
.await(storeDataCategories);
let KICK_STARTER_DATA;
let MOVIE_DATA;
let VIDEO_GAME_DATA;
let currentDataSet;
function storeDataCategories(error, kickStarter, movies, videoGames) {
// console.log(...arguments);
if(error) throw error;
KICK_STARTER_DATA = kickStarter;
MOVIE_DATA = movies;
VIDEO_GAME_DATA = videoGames;
currentDataSet = kickStarter;
buildTreemap();
buildLegend();
}
var svg = d3.select('svg#canvas')
.attr('width', width)
.attr('height', height)
.style('background-color', 'ghostwhite')
.style('box-shadow', '0 6px 26px darkgray');
var fader = (color) => {
return d3.interpolateRgb(color, 'ghostwhite')(0.2);
},
test = d3.schemeCategory20.map(fader),
colorScale = d3.scaleOrdinal(d3.schemeCategory20.map(fader)),
format = d3.format(',d');
var treemap = d3.treemap()
.tile(d3.treemapResquarify)
.size([width, height]);
// Adding tooltip for info on hover
const tooltip = d3.select('svg#canvas')
.append('div')
.attr('id', 'tooltip')
.attr('width', 60 + 'px')
.attr('height', 40 + 'px')
.style('z-index', 10)
.style('display', 'none')
.style('position', 'absolute');
function buildTreemap() {
// console.log(KICK_STARTER_DATA);
// console.log(MOVIE_DATA);
// console.log(VIDEO_GAME_DATA);
// console.log(currentDataSet);
var root = d3.hierarchy(currentDataSet)
.sum(sumValue)
.sort((a, b) => b.height - a.height || b.value - a.value);
treemap(root);
var cell = svg.selectAll('g')
.data(root.leaves())
.enter().append('g')
.attr('transform', d => 'translate(' + [d.x0, d.y0] + ')')
.on('mouseover', d => {
// console.log('mouseover - d:\n', d);
const tooltipText = formatTooltip(d);
tooltip.transition().duration(200)
.style('position', 'absolute')
.style('display', 'block');
tooltip.html(tooltipText)
.attr('data-value', d.value)
.style('top', (d3.event.pageY - 50) + 'px')
.style('left', (d3.event.pageX + 20) + 'px');
})
.on('mouseout', d => {
// console.log('mouseout!');
tooltip.transition().duration(500)
.style('display', 'none');
});
cell.append('rect')
.attr('id', d => d.data.id)
.attr('class', 'tile')
.attr('width', d => d.x1 - d.x0)
.attr('height', d => d.y1 - d.y0)
.attr('fill', d => colorScale(d.parent.data.name))
.attr('data-name', d => d.data.name)
.attr('data-category', d => d.parent.data.name)
.attr('data-value', d => d.data.value);
cell.append('clipPath')
.attr('id', d => `clip-${d.data.name}`)
.append('use')
.attr('xlink:href', d => `#${d.data.name}`);
cell.append('text')
.attr('clip-path', d => `url(#clip-${d.data.name})`)
.selectAll('tspan')
.data(d => d.data.name.split(/(?=[A-Z][^A-Z])/g))
.enter().append('tspan')
.attr('x', 4)
.attr('y', (d, i) => 13 + i * 10)
.text(d => d)
.style('font-size', '10');
}
function buildLegend() {
console.log(`inside 'buildLegend'`);
// console.log(currentDataSet);
const rectWidth = width / 8,
rectHeight = 40;
const legend = d3.select('svg#legend')
.attr('width', width)
.attr('height', rectHeight);
legend.append('g')
.selectAll('g')
.data(colorScale.domain())
.enter()
.append('g')
.attr('class', 'legend')
.append('rect')
.attr('class', 'legend-item')
.attr('width', '40px')
.attr('height', '40px')
.attr('transform', (d, i) => {
return 'translate(' + i * 40 + ',' + 0 + ')';
})
.style('fill', d => colorScale(d))
.style('stroke', d => colorScale(d));
}
function sumValue(d) {
return d.value;
}
function formatTooltip(d) {
const name = d.data.name,
category = d.data.category,
value = d.data.value,
tooltipText = `
<span>Name:</span> ${name} <br>
<span>Category: </span> ${category} <br>
<span>Value:</span> ${value} <br>`;;
console.log('formatted tooltip:\n', tooltipText);
return tooltipText;
}
答案 0 :(得分:0)
轻微错误:您正在将<div>
附加到SVG上,这是行不通的。更改代码并将其附加到主体,这是一个分支:
https://codepen.io/anon/pen/KbVvwR
代码更改:
const tooltip = d3.select('body')
以及一些次要的CSS附加功能:
div#tooltip {
background: #FFF;
pointer-events: none; // important
padding: 4px;
border: 1px solid #CCC;
border-radius: 3px;
}
希望这会有所帮助。