D3.js如何根据子对象数组中是否存在指定项筛选selectAll选中元素
场景说明
你有一个展示星球大战角色待完成项目的环形图网格,对应DOM结构如下:
<div id="LukeSkywalker" class="cell"> <svg> <path class="Get off Tatooine" d=""></path> <path class="Save Princess" d=""></path> <!-- class为项目名 --> <text>Luke Skywalker</text> </svg> </div> <div id="R2D2" class="cell"> <svg> <path class="Find General Kenobi" d=""></path> <!-- 注意原DOM这里有笔误,class属性少了闭合引号,需要先修正 --> <path class="Save Princess" d=""></path> <text>R2-D2</text> </svg> </div> <div id="Emperor" class="cell"> <svg> <path class="Finish Deathstar" d=""></path> <path class="Crush Rebellion" d=""></path> <text>The Emperor</text> <!-- 需要置为淡色的文本 --> </svg> </div>
需求为:hover某个环形图扇区时,将不包含对应项目的环形图的角色名文本置为淡色,比如hoverSave Princess项目时,皇帝的名字要变浅。
问题定位
你遇到的hasProject_test返回空_groups、功能不生效的问题,由以下几个错误导致:
- 项目名获取错误:
event.target.className拿到的是SVG元素的SVGAnimatedString对象,不是普通字符串,直接拿去和项目名匹配必然失败 - 数据绑定依赖问题:如果你的
div.cell没有提前绑定带projects属性的D3数据,filter回调里的d就是undefined,自然筛选不到任何元素 - API和逻辑错误:
- 选到文本元素后调用
append()是新增元素,不是修改现有文本的样式 - 直接用
attr("class", "faded")会覆盖文本原有类,建议用D3的classed方法 - 你写的
donutHasProject逻辑是存在对应项目时返回false,和筛选需求的匹配逻辑相反
- 选到文本元素后调用
修正方案
可以直接基于DOM结构做判断,不需要依赖绑定的projects数据,更稳定不容易出错:
第一步:先加对应样式
.faded { opacity: 0.3; transition: opacity 0.2s ease; }
第二步:替换hover函数实现
function hover(event, d) { // 正确获取当前hover的项目名 const targetProject = event.target.getAttribute('class').trim(); // 先重置所有文本的淡色状态,避免多次hover状态错乱 d3.selectAll('div.cell text').classed('faded', false); // 筛选出没有对应项目的cell,给文本加淡色类 d3.selectAll('div.cell') .filter(function() { // 直接查询当前cell下是否存在对应class的path return d3.select(this).select(`path[class="${targetProject}"]`).empty(); }) .select('text') .classed('faded', true); }
如果要保留你原来基于绑定数据的实现,只需要修改两个地方:
- 把
const className = event.target.className改成const className = event.target.className.baseVal - 给所有
div.cell绑定带projects属性的对应数据,保证filter回调里的d.projects能正常访问
内容的提问来源于stack exchange,提问作者kalmdown
相关产品推荐
相关产品推荐

