В D3.js механизм enter-выборки отвечает за создание DOM-элементов для данных, которые ещё не имеют соответствующих визуальных узлов. Этот процесс является частью общего паттерна data join, в котором данные сопоставляются с элементами DOM, а результат делится на три группы: enter, update и exit.
Основой работы является метод .data(), который принимает
массив данных и сопоставляет его с текущими DOM-элементами выбранной
группы.
const selection = d3.select("svg")
.selectAll("circle")
.data(data);
На этом этапе происходит логическое сопоставление:
Enter-выборка представляет собой особую структуру, содержащую “виртуальные” элементы, которые ещё не существуют в DOM, но уже связаны с данными.
Результат вызова .enter() возвращает объект, содержащий
недостающие данные, ожидающие привязки к DOM:
const enterSelection = selection.enter();
Каждый элемент enter-выборки — это placeholder, содержащий:
Важно понимать, что enter-выборка сама по себе не создаёт DOM-элементы. Она лишь описывает, что должно быть создано.
Создание DOM происходит через .append() или
.insert(), применяемые к enter-выборке.
d3.select("svg")
.selectAll("circle")
.data(data)
.enter()
.append("circle");
На этом этапе каждый элемент enter-выборки преобразуется в реальный DOM-узел.
После вызова .append() возвращается уже полноценная
selection, содержащая созданные элементы, с которыми можно продолжать
работу:
d3.select("svg")
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", d => d.r);
Здесь каждое новое значение из массива данных превращается в
SVG-элемент <circle>.
При вызове .data() D3 выполняет сопоставление по индексу
массива, если не задан ключевой доступ:
.data(data)
Без ключевой функции поведение определяется порядком:
Если данных больше, чем DOM-элементов, лишние элементы попадают в enter.
При использовании ключевой функции поведение меняется:
.data(data, d => d.id)
В этом случае D3 пытается сопоставить элементы по идентификатору, а не по позиции. Это критично для динамических интерфейсов, где порядок может меняться.
Ключевая функция влияет на то, какие элементы считаются новыми.
Пример:
const data = [
{ id: 1, value: 10 },
{ id: 2, value: 20 }
];
selection.data(data, d => d.id);
Если ранее существовал элемент с id: 1, он попадёт в
update. Если появился id: 3, он окажется в enter.
Использование ключей предотвращает:
Классический шаблон построения визуализации:
const circles = d3.select("svg")
.selectAll("circle")
.data(data);
circles.enter()
.append("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", d => d.r);
Здесь логика разделена:
data() выполняет сопоставлениеenter() выделяет новые элементыappend() создаёт DOMattr() задаёт свойстваТакой подход делает процесс предсказуемым и прозрачным.
После создания элементов enter-выборка часто объединяется с update-выборкой для применения общих изменений.
const circles = d3.select("svg")
.selectAll("circle")
.data(data);
circles.enter()
.append("circle")
.merge(circles)
.attr("fill", "steelblue");
Метод .merge() объединяет:
Это позволяет избежать дублирования кода при обновлении атрибутов.
В более новых версиях D3 используется .join(),
инкапсулирующий enter/update/exit логику:
d3.select("svg")
.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", d => d.r);
Внутри .join() автоматически создаётся enter-выборка и
выполняется .append().
Несмотря на это, понимание enter-выборки остаётся критически важным,
так как .join() является лишь абстракцией поверх базовой
модели.
Enter-выборка сохраняет порядок данных. Каждый элемент получает индекс:
selection.enter()
.append("rect")
.attr("x", (d, i) => i * 10);
Здесь i — индекс нового элемента в наборе enter.
Это позволяет:
Enter-выборка может применяться не только к простым элементам, но и к сложным структурам:
const groups = d3.select("svg")
.selectAll("g")
.data(data)
.enter()
.append("g");
groups.append("circle")
.attr("r", d => d.r);
groups.append("text")
.text(d => d.label);
Здесь каждый элемент enter создаёт группу <g>,
внутри которой размещаются дочерние элементы.
Такой подход используется для:
При повторном вызове .data() enter-выборка
пересчитывается заново:
selection.data(newData)
.enter()
.append("circle");
Если количество данных увеличилось, появятся новые элементы. Если уменьшилось, enter будет пустой.
Важно учитывать, что enter не хранит состояние между вызовами — он всегда вычисляется заново на основе текущего DOM и входных данных.
Одной из частых ошибок является попытка использовать enter-выборку после того, как она была “потеряна”:
const enter = selection.enter();
selection.attr("fill", "red"); // enter уже не используется
Правильный подход:
selection.enter()
.append("circle")
.attr("fill", "red");
Другой распространённый случай — забывание .data(), что
приводит к пустой enter-выборке или некорректному сопоставлению.
Enter-выборка является фундаментальным механизмом декларативного построения визуализаций. Она позволяет:
Без понимания enter-выборки невозможно эффективно работать с динамическими графиками, поскольку именно она отвечает за появление новых визуальных объектов в ответ на изменения данных.