В модели связывания данных D3.js ключевым механизмом является тройка enter – update – exit, определяющая жизненный цикл визуальных элементов относительно набора данных. Exit-подвыборка отвечает за ситуацию, когда элементов DOM больше, чем элементов данных, и необходимо корректно удалить лишние узлы.
Связывание данных в D3 строится вокруг метода .data(),
который сопоставляет массив данных с выборкой DOM-элементов. Результатом
этого сопоставления является объект join, содержащий три логические
подвыборки:
Exit-подвыборка формируется автоматически, когда длина текущей выборки DOM превышает длину входного массива данных.
const update = d3.select("ul")
.selectAll("li")
.data(data);
После выполнения .data(data) D3 сравнивает старую и
новую длину массива и формирует update.enter() и
update.exit().
Exit-выборка извлекается через метод .exit():
const exitSelection = d3.select("ul")
.selectAll("li")
.data(data)
.exit();
На этом этапе exitSelection содержит DOM-элементы,
которые больше не связаны с данными.
Основная операция для exit-подвыборки — удаление узлов из DOM с
помощью .remove():
d3.select("ul")
.selectAll("li")
.data(data)
.exit()
.remove();
Метод .remove() удаляет каждый элемент exit-подвыборки
из DOM, полностью очищая визуализацию от устаревших данных.
Корректная работа с динамическими данными требует обработки всех трёх подвыборок.
const selection = d3.select("ul")
.selectAll("li")
.data(data);
// EXIT
selection.exit().remove();
// UPDATE
selection
.text(d => d);
// ENTER
selection.enter()
.append("li")
.text(d => d);
Такой порядок гарантирует:
В более современных подходах D3 используется метод
.merge() для объединения enter и update-подвыборок в единый
поток обновления.
const selection = d3.select("ul")
.selectAll("li")
.data(data);
// EXIT
selection.exit().remove();
// ENTER + UPDATE
selection.enter()
.append("li")
.merge(selection)
.text(d => d);
Exit-логика остаётся отдельной, так как её задача — исключительно удаление.
Exit-подвыборка проявляется только при уменьшении количества элементов данных.
Исходное состояние:
data = [10, 20, 30, 40, 50];
DOM содержит 5 элементов <li>.
После обновления:
data = [10, 20];
D3 формирует:
selection.exit().remove();
Результат — DOM синхронизирован с данными.
Без ключевой функции D3 сопоставляет данные по индексу. Это может приводить к неожиданному удалению или сохранению элементов при перестановке данных.
const selection = d3.select("ul")
.selectAll("li")
.data(data, d => d.id);
id сохраняютсяExit-подвыборка особенно важна при работе с SVG, где каждый элемент может быть сложной графической сущностью.
const circles = d3.select("svg")
.selectAll("circle")
.data(data, d => d.id);
circles.exit()
.transition()
.duration(300)
.attr("r", 0)
.remove();
В данном случае удаление сопровождается анимацией:
Exit-подвыборка может использоваться совместно с
transition() для плавного исчезновения элементов.
selection.exit()
.transition()
.duration(500)
.style("opacity", 0)
.remove();
Такой подход позволяет:
При работе с вложенными структурами (например, диаграммы, деревья, графы) exit-логика применяется рекурсивно.
const nodes = svg.selectAll("g.node")
.data(treeData, d => d.id);
nodes.exit()
.transition()
.duration(400)
.attr("transform", d => `translate(${d.x},${d.y})`)
.style("opacity", 0)
.remove();
Удаление здесь затрагивает не только визуальный элемент, но и всю связанную группу SVG-узлов.
.remove()selection.exit();
Без .remove() DOM-элементы остаются в документе и не
очищаются.
selection.exit().text("removed");
selection.exit().remove();
После .remove() дальнейшие операции не имеют смысла.
Без ключа:
.data(data)
D3 может некорректно сопоставлять элементы, что приводит к неожиданным exit-удалениям.
Exit-подвыборка является фундаментом декларативного подхода D3:
Exit гарантирует, что визуализация не накапливает устаревшие элементы и остаётся строго синхронизированной с источником данных.
Удаление большого количества элементов может быть затратным, особенно при сложных SVG-структурах.
Оптимизационные практики:
<g> для уменьшения
количества операций.exit().remove() в одном цикле
обновленияПри потоковых данных (streaming updates) exit используется для ограничения размера визуализации:
const maxPoints = 100;
data.push(newPoint);
data = data.slice(-maxPoints);
selection
.data(data)
.exit()
.remove();
Таким образом поддерживается фиксированное количество визуальных элементов.
При фильтрации данных exit автоматически отражает исключённые элементы.
const filtered = data.filter(d => d.value > 50);
selection
.data(filtered, d => d.id)
.exit()
.remove();
Все элементы, не прошедшие фильтр, удаляются из DOM.
Exit-подвыборка обеспечивает строгую синхронизацию визуального состояния с данными, удаляя устаревшие элементы и предотвращая накопление лишнего DOM. Она является неотъемлемой частью data-driven подхода, в котором обновление интерфейса определяется изменением данных, а не ручным управлением элементами.