英文:
d3.js Insert() function adds child element - instead of a sibling element
问题
看起来当我使用“insert”函数时,新元素总是作为子元素插入 - 而不是作为同级元素插入。
我仍然不明白为什么会这样,因为插入函数实际上应该在指定的元素之前插入一个新元素 - 而不是在指定的元素内部作为子元素。
基本上,我正在尝试在“text”元素旁边创建一个“rect”元素。
目标是在我的d3 sankey图中的文本背后放置一个背景颜色。
// 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})`;
})
);
英文:
It looks like when I use the “insert” function, the new element is always inserted as a child – Not as a sibling.
I still don't understand why this is, since the insert function should actually insert a new element before the specified element - and not in the specified element as a child.
Basically I am trying to create a "rect" element next to the "text" elements
The goal is to put a background color behind my texts in my d3 sankey diagram.
// 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();
});
}
Here is a picture of the rendered HTML elements from my browser.
Notice that the “rect” element is a child of the text and not a sibling as desired.
答案1
得分: 2
以下是您要翻译的部分:
在您的代码中,结果是预期的:这是因为在一组文本的选择上调用了insert
,它充当父元素。
第一个简单的解决方案是打破链条,这样您就有了selection.append
和selection.insert
。例如:
const svg = d3.select("svg"),
data = d3.range(5).map(e => ({
value: e
}));
svg.selectAll(null)
.data(data)
.join(enter => {
enter.append("text")
.attr("x", 20)
.attr("y", d => 20 + 30 * d.value)
.text(d => `text #${d.value}`)
.call(getTextBox);
enter.insert("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')
})
function getTextBox(selection) {
selection.each(function(d) {
d.bbox = this.getBBox();
});
}
然而,这个SVG将具有以下结构:
<rect></rect>
<rect></rect>
<rect></rect>
...
<text></text>
<text></text>
<text></text>
...
也就是说,如果您希望得到以下结构...
<rect></rect>
<text></text>
<rect></rect>
<text></text>
<rect></rect>
<text></text>
...
... 那么您需要更详细的insert
。在这个示例中,我通过它们的ID获取了相应的文本元素:
const svg = d3.select("svg"),
data = d3.range(5).map(e => ({
value: e
}));
svg.selectAll(null)
.data(data)
.join(enter => {
enter.append("text")
.attr("id", d => `text${d.value}`)
.attr("x", 20)
.attr("y", d => 20 + 30 * d.value)
.text(d => `text #${d.value}`)
.call(getTextBox);
enter.insert("rect", d => d3.select(`#text${d.value}`).node())
.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')
})
function getTextBox(selection) {
selection.each(function(d) {
d.bbox = this.getBBox();
});
}
希望这些翻译对您有所帮助。
英文:
The result in the code you have is expected: it happens because the insert
is called on a selection of texts, which acts as a parent.
The first, simple solution, is breaking the chain, so you have a selection.append
and a selection.insert
. For instance:
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
const svg = d3.select("svg"),
data = d3.range(5).map(e => ({
value: e
}));
svg.selectAll(null)
.data(data)
.join(enter => {
enter.append("text")
.attr("x", 20)
.attr("y", d => 20 + 30 * d.value)
.text(d => `text #${d.value}`)
.call(getTextBox);
enter.insert("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')
})
function getTextBox(selection) {
selection.each(function(d) {
d.bbox = this.getBBox();
});
}
<!-- language: lang-html -->
<script src="https://d3js.org/d3.v7.min.js"></script>
<svg></svg>
<!-- end snippet -->
However, this SVG will have this structure:
<rect></rect>
<rect></rect>
<rect></rect>
...
<text></text>
<text></text>
<text></text>
...
That said, if you want the following structure instead...
<rect></rect>
<text></text>
<rect></rect>
<text></text>
<rect></rect>
<text></text>
...
... you'll need a bit more elaborated insert
. In this example I'm getting the respective text elements by their IDs:
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
const svg = d3.select("svg"),
data = d3.range(5).map(e => ({
value: e
}));
svg.selectAll(null)
.data(data)
.join(enter => {
enter.append("text")
.attr("id", d => `text${d.value}`)
.attr("x", 20)
.attr("y", d => 20 + 30 * d.value)
.text(d => `text #${d.value}`)
.call(getTextBox);
enter.insert("rect", d => d3.select(`#text${d.value}`).node())
.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')
})
function getTextBox(selection) {
selection.each(function(d) {
d.bbox = this.getBBox();
});
}
<!-- language: lang-html -->
<script src="https://d3js.org/d3.v7.min.js"></script>
<svg></svg>
<!-- end snippet -->
答案2
得分: 0
在数据上进行迭代,并将文本/矩形绑定在父包装器中。
希望对你有所帮助:链接。
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论