Выделение DOM-элементов в D3.js строится вокруг концепции селекций — абстракции над набором узлов документа, позволяющей применять к ним операции как к единой группе. Селекции являются фундаментом всей библиотеки, поскольку через них выполняется связывание данных, изменение атрибутов, стилизация и обработка событий.
Селекция создаётся через методы:
d3.select() — выбирает первый подходящий элементd3.selectAll() — выбирает все элементы, соответствующие
селектору CSSconst container = d3.select("#chart");
const circles = d3.selectAll("circle");
Каждая селекция возвращает объект, содержащий массив DOM-узлов и методы для их модификации. При этом операции выполняются не по одному элементу, а сразу по всей группе.
Ключевая особенность — цепочечный интерфейс. Практически любой метод возвращает новую селекцию, что позволяет строить последовательные преобразования:
d3.select("svg")
.attr("width", 600)
.attr("height", 400)
.style("background-color", "#f5f5f5");
Селекции становятся особенно мощными при связывании с данными через
метод data().
const data = [10, 20, 30];
d3.selectAll("circle")
.data(data);
После привязки каждый элемент получает доступ к соответствующему значению. Это используется для динамического построения визуализаций.
Методы enter(), update() и
exit() управляют жизненным циклом элементов:
enter() — новые данные без соответствующих
DOM-элементовupdate() — существующие элементыexit() — лишние элементыconst circles = d3.select("svg")
.selectAll("circle")
.data(data);
circles.enter()
.append("circle")
.attr("r", d => d);
Изменение внешнего вида выполняется через attr() и
style().
d3.select("circle")
.attr("cx", 100)
.attr("cy", 100)
.attr("r", 50);
Атрибуты могут задаваться функциями, принимающими данные и индекс элемента:
d3.selectAll("circle")
.attr("r", (d, i) => d * 2);
d3.selectAll("circle")
.style("fill", "steelblue")
.style("opacity", 0.8);
Функциональный подход позволяет динамически вычислять стили:
.style("fill", d => d > 50 ? "red" : "green");
Селекции позволяют назначать обработчики событий напрямую:
d3.selectAll("circle")
.on("click", function(event, d) {
console.log("Значение:", d);
});
Контекст this внутри обработчика указывает на
DOM-элемент, а event содержит информацию о событии.
Наиболее часто используемые события:
mouseovermouseoutmousemoveclickПодсветка элементов при наведении реализуется через сочетание событий
mouseover и mouseout. Основная идея
заключается в изменении стилей активного элемента и возврате исходного
состояния при уходе курсора.
d3.selectAll("circle")
.on("mouseover", function() {
d3.select(this)
.style("fill", "orange")
.style("stroke", "black")
.style("stroke-width", 2);
})
.on("mouseout", function() {
d3.select(this)
.style("fill", "steelblue")
.style("stroke", "none");
});
Здесь используется d3.select(this), позволяющий выделить
текущий элемент из всей группы.
Более устойчивый подход основан на управлении CSS-классами:
d3.selectAll("circle")
.on("mouseover", function() {
d3.select(this).classed("hovered", true);
})
.on("mouseout", function() {
d3.select(this).classed("hovered", false);
});
CSS:
.hovered {
fill: orange;
stroke: black;
stroke-width: 2px;
}
Такой способ отделяет логику от представления и упрощает поддержку.
Подсветка может затрагивать не только один элемент, но и связанные элементы. Это достигается через использование данных.
d3.selectAll("circle")
.on("mouseover", function(event, d) {
d3.selectAll("circle")
.style("opacity", c => c === d ? 1 : 0.3);
})
.on("mouseout", function() {
d3.selectAll("circle")
.style("opacity", 1);
});
Такой подход позволяет реализовывать эффекты фокусировки на одном значении среди множества.
Каждая функция в D3 получает три аргумента:
d — данныеi — индекс элементаnodes — массив узловd3.selectAll("circle")
.attr("cx", (d, i) => i * 40 + 20);
Индекс часто используется для позиционирования или создания визуальной иерархии.
Подсветка становится более выразительной при использовании переходов:
d3.selectAll("circle")
.on("mouseover", function() {
d3.select(this)
.transition()
.duration(200)
.attr("r", 12);
})
.on("mouseout", function() {
d3.select(this)
.transition()
.duration(200)
.attr("r", 6);
});
Плавные изменения визуального состояния улучшают восприятие интерактивности.
При большом количестве элементов используется делегирование через общий контейнер:
d3.select("svg")
.on("mouseover", function(event) {
const target = event.target;
if (target.tagName === "circle") {
d3.select(target)
.style("fill", "orange");
}
});
Такой подход снижает количество обработчиков и повышает производительность при работе с большими наборами данных.
Существует несколько подходов:
Выбор зависит от сложности сцены и объёма данных.
Правильная организация селекций и событий позволяет избегать деградации производительности и повышает предсказуемость поведения визуализации.