В основе модели обновления данных в D3.js лежит концепция связывания данных с DOM-элементами. Этот процесс называется data join и формально описывает соответствие между массивом данных и набором элементов в DOM. При каждом обновлении визуализации библиотека сравнивает:
Результатом становится разделение на три логические группы:
Эта модель позволяет управлять изменениями декларативно, не пересоздавая весь DOM при каждом обновлении.
Операция связывания выполняется через метод
selection.data():
const circles = svg.selectAll("circle")
.data(data);
На этом этапе D3 не изменяет DOM, а только устанавливает соответствие между:
Возвращаемое значение содержит внутреннюю структуру с тремя подселекциями:
enter()exit()Enter-подвыборка содержит фиктивные элементы, представляющие данные, для которых ещё нет DOM-узлов.
Типичный паттерн:
const circles = svg.selectAll("circle")
.data(data);
circles.enter()
.append("circle")
.attr("r", 5);
Механика:
enter() соответствует одному новому
элементу данныхappend() создаёт реальные DOM-узлыappend() элементы становятся частью основной
selectionОсобенность enter-селекции заключается в том, что она не содержит реальных DOM-элементов до момента их создания.
Update — это основная selection, возвращаемая после
.data().
const circles = svg.selectAll("circle")
.data(data);
circles
.attr("fill", "steelblue")
.attr("r", d => d.value);
В update-подвыборке находятся элементы, которые уже существуют в DOM и были сопоставлены с новыми данными.
Характерные особенности:
Exit-подвыборка содержит DOM-элементы, для которых отсутствуют соответствующие данные.
circles.exit()
.remove();
Логика работы:
.remove()Exit — ключевой механизм поддержания согласованности визуализации с данными.
Типичный полный шаблон обновления:
const circles = svg.selectAll("circle")
.data(data);
// EXIT
circles.exit()
.remove();
// UPDATE
circles
.attr("fill", "steelblue")
.attr("r", d => d.value);
// ENTER
circles.enter()
.append("circle")
.attr("fill", "green")
.attr("r", d => d.value)
.merge(circles);
Важно разделение ответственности:
Метод merge() объединяет enter и update selections в
единый поток обновления.
const circles = svg.selectAll("circle")
.data(data);
const circlesEnter = circles.enter()
.append("circle");
circlesEnter.merge(circles)
.attr("r", d => d.value);
Смысл:
По умолчанию D3 сопоставляет данные по индексу массива. Это может приводить к некорректным обновлениям при изменении порядка элементов.
Для стабильной идентификации используется key function:
const circles = svg.selectAll("circle")
.data(data, d => d.id);
Роль key function:
При наличии ключей D3 сравнивает не позиции, а идентификаторы.
Модель можно представить как последовательность:
Исходное состояние DOM
Новый массив данных
Сопоставление data join
Разделение:
Это превращает визуализацию в функцию состояния:
UI = f(data)
В сложных визуализациях применяется структурированный подход, известный как general update pattern:
function update(data) {
const circles = svg.selectAll("circle")
.data(data, d => d.id);
circles.exit()
.transition()
.attr("r", 0)
.remove();
circles.transition()
.attr("fill", "steelblue")
.attr("r", d => d.value);
circles.enter()
.append("circle")
.attr("r", 0)
.attr("fill", "green")
.merge(circles)
.transition()
.attr("r", d => d.value);
}
Ключевые характеристики:
Внутри D3 последовательность операций критична:
.data() создаёт привязку.enter() вычисляется относительно отсутствующих
узлов.exit() вычисляется как обратное множествоВажно, что enter и exit не существуют как DOM-элементы до применения операций создания или удаления.
Рассмотрим три типичных случая:
Enter-update-exit формирует переходную модель между императивным DOM и декларативным описанием данных:
Эта модель позволяет строить визуализации, где изменения данных автоматически транслируются в структурные изменения интерфейса без ручного управления каждым DOM-узлом.