Каждый раз, когда вы обновляете диаграмму, вы запускаете это:
const update = group
.append("g") // create a new g
.selectAll("rect") // select all the rectangles in that g (which are none)
.data(randomData);
update
теперь пустой выбор, нет rect
s для выбора во вновь созданном g
. Поэтому, когда мы используем update.enter()
, элемент DOM создается для каждого элемента в массиве данных. Использование Enter создаст элемент для каждого элемента в массиве данных, который еще не имеет соответствующего элемента.
update.exit()
будет пустым, поскольку в update
не выбрано ни одного элемента, поэтому ничего не будет удалено. Ранее созданные полосы не затрагиваются, вы их не выбираете.
Если мы изменим ваш код просто для удаления .append("g")
, это приблизит нас к работе ( например ). Полосы были окрашены в белый цвет, поэтому они не были видны, я изменил цвет заливки, чтобы выбор обновления был видимым
Если мы удалим .append("g")
, у нас есть некоторые другие проблемы при обновлении:
- вы не выходите из текста (поскольку вы не выбираете
text
с .selectAll()
, только rect
элементами), а - вы объединение текста и прямоугольников в одну выделенную область, что немного проблематично c, если вы хотите расположить и покрасить их по-разному при обновлении.
Вторая проблема может быть объяснена немного подробнее:
update.enter().append("text") // returns a selection of newly created text elements
.merge(update) // merges the selection of newly created text with existing rectangles
.attr("fill", .... // affects both text and rects.
Эти две проблемы могут быть решены при правильном использовании цикла ввода / обновления / выхода.
Следует отметить, что шаблон выхода обновления ввода D3 не предназначен для ввода элементов более одного раза с одним и тем же оператором, вы вводите текст и выполняете инструкции с одним и тем же оператором ввода, см. Здесь .
Таким образом, один из вариантов - использовать две выборки, одну для текста и одну для ректов:
const updateRect = group
.selectAll("rect")
.data(randomData);
let bars = updateRect
.enter() // create new dom elements for added data items
.append("rect")
.merge(updateRect)
.attr("x", (d, i) => i * (width / randomData.length))
.attr("y", d => height - d * 5)
.attr("width", width / randomData.length - padding)
.attr("height", d => d * 5)
.attr("fill", d => colorGradient(d));
const updateText = group
.selectAll("text")
.data(randomData);
let labels = updateText
.enter()
.append("text")
.merge(updateText)
.text(d => d)
.attr("text-anchor", "middle")
.attr(
"x",
(d, i) =>
i * (width / randomData.length) +
(width / randomData.length - padding) / 2
)
.attr("y", d => height - d * 5 + 12)
.style("font-family", "sans-serif")
.style("font-size", 12)
.style("fill", "#fff");
updateRect.exit().remove();
updateText.exit().remove();
Здесь в песочнице форме.
Другой вариант - использовать родительский g
для хранения как прямоугольника, так и текста, это может быть сделано разными способами, но если вам не нужен переход между значениями или количеством столбцов, вероятно, будет наиболее просто, например:
const update = group
.selectAll("g")
.data(randomData);
// add a g for every extra datum
const enter = update.enter().append("g")
// give them a rect and text element:
enter.append("rect");
enter.append("text");
// merge update and enter:
const bars = update.merge(enter);
// modify the rects
bars.select("rect")
.attr("x", (d, i) => i * (width / randomData.length))
.attr("y", d => height - d * 5)
.attr("width", width / randomData.length - padding)
.attr("height", d => d * 5)
.attr("fill", d => { return colorGradient(d)});
// modify the texts:
bars.select("text")
.text(d => d)
.attr("text-anchor", "middle")
.attr(
"x",
(d, i) =>
i * (width / randomData.length) +
(width / randomData.length - padding) / 2
)
.attr("y", d => height - d * 5 + 12)
.style("font-family", "sans-serif")
.style("font-size", 12)
.style("fill", "#ffffff");
Вот что в sandox форме.
Немного дальнейшего объяснения: selection.select()
выбирает первый соответствующий элемент для каждого элемента в selection - чтобы мы могли выбрать единственный прямоугольник в каждом родительском элементе g (который мы добавляем при вводе родительского элемента) с bars.select("rect")
выше D3 передает родительские данные ребенку при добавлении выше. Примечание. Если бы у нас были вложенные данные (несколько столбцов или текстов на элемент массива данных), нам бы потребовались вложенные циклы ввода / вывода / обновления .