D3.js choropleth地图tooltip鼠标悬停显示重复数据问题求助
问题描述
我正在完成freeCodeCamp的人口 choropleth 地图项目,这是一个展示美国郡级教育数据的地图。目前除自定义tooltip外所有功能均正常:鼠标悬停时,tooltip本应显示对应郡的名称、教育百分比等信息,但实际无论悬停哪个郡,都只显示同一个郡的信息。不过使用d3原生title tooltip时数据显示正常。我怀疑问题出在.data(dataset).enter()调用引用的数据集不匹配上,以下是代码片段,问题代码已在底部标注:
// Fetch the population/education dataset and feed it into a function that will merge it with the map dataset fetch("https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/for_user_education.json") .then(resp => resp.json()) .then(popData => dataMerge(popData)) // Merge datasets -- this fetch is for the map geometry data function dataMerge(popData) { fetch("https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/counties.json") .then(resp => resp.json()) .then(geoData => { const counties = geoData.objects.counties.geometries; popData.forEach(elem => { let fips = elem.fips; for(let i=0; i<counties.length; i++) { let id = counties[i].id; if(id === fips) { counties[i] = Object.assign(counties[i], elem); break; } } }) return geoData; }) .then(geoData => svgGenerate(geoData)) } // Generate the svg function svgGenerate(data) { let geojson = topojson.feature(data, data.objects.counties); let statesjson = topojson.feature(data, data.objects.states) // let projection = d3.geoAlbers(); const path = d3.geoPath(); const canvas = svg.append('g') // .attr('padding', padding) canvas.selectAll('path') .data(geojson.features) .enter() .append('path') .attr('d', path) .attr('class', 'county') // since I'm using the geojson.features data, I must pull the county data explicitly. .attr('data-fips', (d,i)=>data.objects.counties.geometries[i].fips) .attr('data-education', (d,i)=>data.objects.counties.geometries[i].bachelorsOrHigher) .attr('fill', (d,i) => { const percentage = data.objects.counties.geometries[i].bachelorsOrHigher if(percentage<10) return 'rgb(255, 255, 255)' else if(percentage<20) return 'rgb(223, 254, 240)' else if(percentage<30) return 'rgb(187, 255, 226)' else if(percentage<40) return 'rgb(131, 255, 202)' else if(percentage<50) return 'rgb(0, 255, 146)' else if(percentage<60) return 'rgb(5, 215, 126)' else if(percentage<70) return 'rgb(1, 99, 57)' else return 'black' }) .attr('transform', 'translate(160,30)') // ****** HERE IS WHAT FAILS ******* .on('mouseenter', (e,d) => { tooltip.style('visibility','visible') .style('left', event.pageX+'px') .style('bottom', h-event.pageY+300+'px') .html((d,i) => `${data.objects.counties.geometries[i].area_name}<br/>${data.objects.counties.geometries[i].bachelorsOrHigher}%`) .attr('data-education',(d,i)=>data.objects.counties.geometries[i].bachelorsOrHigher) }) .on('mouseout', (e,d) => { tooltip.style('visibility','hidden') }) .append('title') .text((d,i)=>data.objects.counties.geometries[i].fips)
问题原因及修复方案
问题核心是你在mouseenter事件回调中,错误地通过索引i访问原数据集data.objects.counties.geometries,但此处的i并非当前元素绑定数据的索引——D3事件回调的第二个参数d已经是当前元素绑定的完整数据对象,无需再通过原数据集索引查找。同时你混用了事件参数e和全局event,可能引发兼容性问题。
修复后的鼠标事件代码:
// ****** 修复后的鼠标事件代码 ******* .on('mouseenter', (e, d) => { tooltip.style('visibility','visible') .style('left', `${e.pageX}px`) .style('bottom', `${h - e.pageY + 300}px`) // 直接使用绑定的数据对象d,已包含合并后的教育数据 .html(`${d.properties.area_name}<br/>${d.properties.bachelorsOrHigher}%`) .attr('data-education', d.properties.bachelorsOrHigher) }) .on('mouseout', (e, d) => { tooltip.style('visibility','hidden') }) .append('title') // 直接使用绑定的数据 .text(d => d.properties.fips)
额外优化建议:
你之前设置data-fips、data-education和填充色时,也无需通过data.objects.counties.geometries[i]获取数据,直接使用绑定的d.properties即可,示例:
.attr('data-fips', d => d.properties.fips) .attr('data-education', d => d.properties.bachelorsOrHigher) .attr('fill', d => { const percentage = d.properties.bachelorsOrHigher // 原有颜色判断逻辑不变 })
这样能简化代码,彻底避免索引不匹配的问题。
内容的提问来源于stack exchange,提问作者u-lx
相关产品推荐
相关产品推荐

