D3.js中insert()函数添加子元素而非兄弟元素的问题求助
D3.js insert() 创建rect为text子元素的问题解决
在使用D3.js的insert()函数时,新创建的rect元素被插入成了text元素的子元素,而非预期的兄弟元素。我的需求是给桑基图的文本添加背景色,需要让rect和text成为同级结构。
原代码
// NODE TEXT this.nodes .selectAll('text') .data(this.data.nodes) .join( (enter) => enter .append('text') .text((d) => `${d.name + ': ' + d.value}`) .style('fill', '#000000') .attr('text-anchor', 'right') .attr('x', (d, i) => { return d.x0 + (d.x1 - d.x0) / 0.5; }) .attr('y', (d, i) => { return d.y0 + (d.y1 - d.y0) / 2; }) .attr('dy', '0.35em') .attr('transform', (d, i) => { const x = d.x0 + (d.x1 - d.x0) / 2; const y = d.y0 + (d.y1 - d.y0) / 2; return `rotate(0, ${x}, ${y})`; }) .call(getTextBox) .insert('rect', 'text') .attr('width', function (d) { return d.bbox.width; }) .attr('height', function (d) { return d.bbox.height; }) .attr('x', function (d) { return d.bbox.x; }) .attr('y', function (d) { return d.bbox.y; }) .style('fill', 'grey'), (update) => update .transition() .duration(1000) .ease(d3_easeQuadInOut) .attr('x', (d, i) => { return d.x0 + (d.x1 - d.x0) / 2; }) .attr('y', (d, i) => { return d.y0 + (d.y1 - d.y0) / 2; }) .attr('transform', (d, i) => { const x = d.x0 + (d.x1 - d.x0) / 2; const y = d.y0 + (d.y1 - d.y0) / 2; return `rotate(0, ${x}, ${y})`; }) ); function getTextBox(selection) { selection.each(function (d) { d.bbox = this.getBBox(); }); }
问题原因
链式调用的上下文错误:你在enter.append('text')之后调用insert('rect', 'text'),此时选择集指向的是text元素,所以insert会把rect作为子元素插入到text内部,而非父容器下的兄弟元素。
解决方案
方案1:先插入rect背景(推荐,保证背景在文本下层)
调整元素创建顺序,先在父容器中插入rect,再创建text,最后用text的bbox更新rect的尺寸和位置:
(enter) => { // 先为每个数据项在父容器插入rect const rects = this.nodes.insert('rect', 'text') .data(this.data.nodes) .style('fill', 'grey') .attr('width', 0) .attr('height', 0); // 创建text元素并获取bbox const texts = enter.append('text') .text(d => `${d.name}: ${d.value}`) .style('fill', '#000000') .attr('text-anchor', 'right') .attr('x', (d) => d.x0 + (d.x1 - d.x0) / 0.5) .attr('y', (d) => d.y0 + (d.y1 - d.y0) / 2) .attr('dy', '0.35em') .attr('transform', (d) => { const x = d.x0 + (d.x1 - d.x0) / 2; const y = d.y0 + (d.y1 - d.y0) / 2; return `rotate(0, ${x}, ${y})`; }) .call(getTextBox); // 用text的bbox数据更新rect rects.data(texts.data()) .attr('width', d => d.bbox.width) .attr('height', d => d.bbox.height) .attr('x', d => d.bbox.x) .attr('y', d => d.bbox.y); return texts; // join需要返回enter阶段的主元素 },
方案2:切换选择集到父容器后插入rect
在调用insert前,先切换选择集到父元素,再指定插入到text之前:
enter.append('text') // ... 保留原text的所有属性设置 ... .call(getTextBox) .select(function() { return this.parentNode; }) // 切换到父容器元素 .insert('rect', 'text') // 在父容器下插入rect到text之前 .attr('width', d => d.bbox.width) .attr('height', d => d.bbox.height) .attr('x', d => d.bbox.x) .attr('y', d => d.bbox.y) .style('fill', 'grey')
注意:SVG按DOM顺序渲染,这种方式rect会在text之后创建,若需要背景在下层,需调整插入顺序或使用z-index相关属性。
内容的提问来源于stack exchange,提问作者1zz0
相关产品推荐
相关产品推荐

