如何在D3.js中点击SVG矩形后嵌套添加另一个SVG图表?
在D3.js中点击矩形后嵌套添加子SVG图表的正确实现
我看了你的代码,发现几个关键问题导致效果不符合预期:
- 函数语法错误:
var createMap (mapData){这种写法不符合JS规范,应该用标准函数定义写法,而且你还混用了mapData和mapdata的拼写 - SVG不能嵌套在
<rect>元素里:矩形是SVG的基础图形元素,无法作为其他SVG的父节点,你需要在主SVG容器中添加子SVG,再把它定位到矩形的位置 - 你把生成的子SVG放到了外部的
#subnodeMapdiv里,而不是目标矩形所在的SVG内部
下面是修正后的完整实现,我会一步步解释关键改动:
修正后的HTML结构
我们只需要保留主SVG的容器,子SVG会直接嵌套在主SVG里,不需要额外的外部div:
<body> <div id="mainMap"> <svg></svg> </div> </body>
修正后的JavaScript代码
// 初始化主SVG容器 var svg = d3.select("#mainMap svg") .attr("width", 600) .attr("height", 400); // 添加可点击的矩形 svg.append('rect') .attr('class', 'content shape') .attr("x", 50) // 给矩形设置明确的x/y位置,方便后续定位子SVG .attr("y", 50) .attr("width", 20) .attr("height", 20) .attr("stroke", "black") .attr("stroke-width", 1) .style("fill", "#fff") .on("click", createMap); // 点击事件处理函数 function createMap() { var rect = d3.select(this); // 检查是否已经存在子SVG,避免重复添加,实现切换效果 var existingSubSvg = rect.parent().select(".sub-svg"); if (existingSubSvg.size() > 0) { existingSubSvg.remove(); rect.attr("width", 20) .attr("height", 20) .style("fill", "#fff"); return; } // 获取矩形的位置,设置子SVG的尺寸 var x = parseFloat(rect.attr("x")); var y = parseFloat(rect.attr("y")); var subWidth = 200; var subHeight = 200; // 放大原矩形(可选,根据你的需求调整) rect.attr("width", subWidth) .attr("height", subHeight) .style("fill", "lightsteelblue"); // 在主SVG中添加子SVG,定位到原矩形的位置 var subSvg = rect.parent() .append("svg") .attr("class", "sub-svg") .attr("x", x) .attr("y", y) .attr("width", subWidth) .attr("height", subHeight); // 调用生成图表的函数,传入子SVG和模拟数据 getGeneratedMap(subSvg, { data: [15, 30, 45, 22, 50] // 这里替换成你的真实业务数据 }); } // 生成子图表的函数,接收子SVG容器和数据作为参数 function getGeneratedMap(subSvg, mapData) { // 这里写你的图表生成逻辑,下面是一个简单的柱状图示例 var padding = 20; // X轴比例尺 var xScale = d3.scaleBand() .domain(d3.range(mapData.data.length)) .range([padding, subSvg.attr("width") - padding]) .padding(0.1); // Y轴比例尺 var yScale = d3.scaleLinear() .domain([0, d3.max(mapData.data)]) .range([subSvg.attr("height") - padding, padding]); // 添加柱状图柱子 subSvg.selectAll("rect.bar") .data(mapData.data) .enter() .append("rect") .attr("class", "bar") .attr("x", function(d, i) { return xScale(i); }) .attr("y", function(d) { return yScale(d); }) .attr("width", xScale.bandwidth()) .attr("height", function(d) { return subSvg.attr("height") - padding - yScale(d); }) .style("fill", "darkorange"); return subSvg; }
关键改动说明
- 修正函数语法:把错误的变量声明式函数改成标准
function定义,统一变量名拼写,避免拼写错误 - 正确嵌套子SVG:子SVG直接添加到主SVG容器中,通过
x和y属性定位到原矩形的位置,符合SVG的嵌套规则 - 实现切换效果:每次点击先检查是否已有子SVG,存在则删除并恢复原矩形状态,避免重复创建
- 传递子SVG容器:
getGeneratedMap函数直接接收子SVG作为参数,在指定容器内绘制图表,不再依赖外部DOM元素 - 明确位置定位:给原矩形设置明确的
x/y属性,确保子SVG能精准对齐到矩形位置
这样点击矩形后,就会在它的位置展开一个包含子图表的SVG,再次点击可以收起恢复原状。
内容的提问来源于stack exchange,提问作者Vaishali Chaudhari
相关产品推荐
相关产品推荐

