Метод .on() в D3.js используется для привязки
обработчиков событий к выбранным DOM-элементам и является ключевым
механизмом интерактивности в визуализациях. Он работает поверх
стандартной модели событий браузера, но тесно интегрирован с системой
выборок D3, данными (datum) и паттернами обновления
DOM.
Общий синтаксис метода:
selection.on(type, listener[, options])
type — строка события (например, "click",
"mouseover", "input")listener — функция обработчика событияoptions — необязательные параметры (например,
управление фазой capture)Привязка события происходит к каждому элементу выборки:
d3.selectAll("circle")
.on("click", function(event, d) {
console.log("Клик по элементу:", d);
});
Каждый элемент, входящий в выборку, получает собственный обработчик, который получает доступ к:
event)d)D3.js связывает данные с DOM-элементами через механизм data join. В
обработчиках .on() это становится основным способом
взаимодействия с визуализацией.
d3.selectAll("rect")
.data([10, 20, 30])
.on("mouseover", function(event, d) {
d3.select(this)
.attr("fill", "orange");
console.log("Значение:", d);
});
Здесь:
this указывает на текущий DOM-элементd — значение, привязанное к элементуevent содержит информацию о событии (координаты, тип и
др.)Метод .on() поддерживает любые DOM-события:
.on("click", handler)
.on("dblclick", handler)
.on("mouseover", handler)
.on("mouseout", handler)
.on("mousemove", handler)
.on("keydown", handler)
.on("keyup", handler)
.on("input", handler)
.on("change", handler)
.on("touchstart", handler)
.on("touchmove", handler)
.on("touchend", handler)
D3 не ограничивает список событий и работает с любыми стандартными DOM event types.
В D3 v6+ обработчик получает event как первый аргумент,
что отличается от старых версий, где использовался глобальный
d3.event.
d3.select("button")
.on("click", function(event) {
console.log(event.type);
});
Данные передаются вторым параметром:
d3.selectAll("li")
.data(["A", "B", "C"])
.on("click", function(event, d) {
console.log(d);
});
this внутри обработчиковКонтекст this сохраняется и указывает на текущий элемент
DOM:
d3.selectAll("path")
.on("mouseover", function(event, d) {
d3.select(this)
.attr("stroke-width", 3);
});
Такой подход позволяет локально модифицировать элементы без повторного поиска через селекторы.
При повторном вызове .on() с тем же типом события
предыдущий обработчик заменяется:
d3.select("div")
.on("click", function() {
console.log("Первый обработчик");
});
d3.select("div")
.on("click", function() {
console.log("Второй обработчик");
});
После выполнения активен только второй обработчик.
Удаление осуществляется передачей null:
d3.selectAll("circle")
.on("click", null);
Это полностью снимает обработчик с выбранных элементов.
D3 поддерживает namespacing событий через точку:
d3.select("svg")
.on("click.zoom", zoomHandler)
.on("click.tooltip", tooltipHandler);
Удаление по пространству имён:
d3.select("svg")
.on("click.zoom", null);
Это позволяет управлять несколькими логическими слоями поведения независимо.
События в D3 следуют стандартной модели DOM: capture → target → bubble.
Остановка всплытия:
d3.selectAll("rect")
.on("click", function(event) {
event.stopPropagation();
});
Отмена стандартного поведения:
d3.select("a")
.on("click", function(event) {
event.preventDefault();
});
При использовании enter/update/exit-паттерна обработчики часто назначаются после создания элементов:
const circles = d3.select("svg")
.selectAll("circle")
.data([5, 10, 15])
.enter()
.append("circle");
circles
.on("click", function(event, d) {
d3.select(this).attr("r", d);
});
Важно, что обработчик привязывается к результату
enter().
Хотя D3 не использует делегирование в классическом jQuery-стиле автоматически, его можно реализовать вручную через общий контейнер:
d3.select("svg")
.on("click", function(event) {
const target = event.target;
if (target.tagName === "circle") {
console.log("Клик по circle");
}
});
Такой подход полезен при большом количестве элементов.
При повторной привязке данных обработчики остаются, если не происходит пересоздания элементов:
const selection = d3.selectAll("rect")
.data([1, 2, 3])
.on("click", handler);
selection.data([4, 5, 6]); // обработчики сохраняются
Если элементы пересоздаются через enter().append(),
обработчики нужно назначать заново.
При большом количестве элементов использование .on() на
каждом узле может создавать нагрузку. В таких случаях применяются
стратегии:
Пример централизованного управления:
d3.select("svg")
.on("mousemove", function(event) {
const [x, y] = d3.pointer(event);
console.log(x, y);
});
Обработчики событий часто комбинируются с
transition():
d3.selectAll("circle")
.on("mouseover", function() {
d3.select(this)
.transition()
.duration(300)
.attr("r", 20);
})
.on("mouseout", function() {
d3.select(this)
.transition()
.duration(300)
.attr("r", 10);
});
События становятся триггерами для анимаций, создавая интерактивные визуальные реакции.
Один элемент может иметь несколько независимых обработчиков:
d3.select("rect")
.on("mouseenter", handlerA)
.on("mouseleave", handlerB)
.on("click", handlerC);
Каждое событие изолировано и не конфликтует с другими.
Часто обработчики содержат условную логику, зависящую от данных:
d3.selectAll("circle")
.on("click", function(event, d) {
if (d > 10) {
d3.select(this).attr("fill", "red");
} else {
d3.select(this).attr("fill", "blue");
}
});
Такой подход позволяет реализовывать интерактивную фильтрацию и визуальные состояния.
Обработчики .on() часто используют замыкания для доступа
к внешним переменным:
const scale = 2;
d3.selectAll("circle")
.on("click", function(event, d) {
d3.select(this)
.attr("r", d * scale);
});
Это позволяет связывать поведение событий с логикой приложения без глобальных переменных.
Событие на одном элементе может влиять на другие элементы выборки:
d3.selectAll("circle")
.on("mouseover", function(event, d) {
d3.selectAll("circle")
.attr("opacity", 0.3);
d3.select(this)
.attr("opacity", 1);
});
Такой паттерн часто используется для подсветки связей и сравнений в данных.