Метод selection.datum() в D3.js используется для
привязки одного значения данных ко всем выбранным DOM-элементам или для
извлечения уже привязанного значения. В отличие от
selection.data(), который работает с массивами данных и
реализует полноценный data join (enter/update/exit),
datum() оперирует единичным значением и не создаёт механизм
сопоставления элементов и массива.
При вызове selection.datum(value) указанное значение
сохраняется в каждом элементе выборки как скрытое свойство
_data_ (внутренняя механика может меняться, но
концептуально данные привязываются напрямую к DOM-узлу). Это значение
становится доступным через аргумент функций-колбэков или через повторный
вызов datum() без параметров.
d3.select("#box")
.datum(42);
После выполнения этого кода элемент #box содержит
привязанное значение 42.
Если вызвать datum() без аргумента:
const value = d3.select("#box").datum();
console.log(value); // 42
Происходит извлечение ранее привязанного значения.
datum() от
data()Ключевое различие заключается в уровне абстракции и сценариях использования.
data():
datum():
Пример различия:
// data(): массив значений
d3.selectAll("circle")
.data([10, 20, 30]);
// datum(): одно значение на все элементы
d3.selectAll("circle")
.datum(10);
Во втором случае все circle получают одно и то же
значение 10.
datum() часто используется для задания общего контекста,
который не зависит от количества элементов.
const config = {
color: "steelblue",
radius: 8
};
d3.select("svg")
.datum(config)
.append("circle")
.attr("r", d => d.radius)
.attr("fill", d => d.color);
Здесь объект config доступен как d в
последующих цепочках. Несмотря на то, что добавляется один элемент,
использование datum() позволяет передавать
структурированные настройки.
Одно из ключевых свойств datum() — данные наследуются
дочерними элементами при создании внутри текущего контекста.
d3.select("svg")
.datum({ scale: 2 })
.append("g")
.append("rect")
.attr("width", d => 50 * d.scale)
.attr("height", d => 50 * d.scale);
В этом примере объект { scale: 2 } доступен внутри
rect, несмотря на то что он был задан на уровне
svg.
Во многих методах D3 доступ к данным осуществляется через первый аргумент функции:
d3.select("#label")
.datum("Hello D3")
.text(d => d);
Значение "Hello D3" передаётся в функцию как
d.
Это особенно полезно, когда требуется унифицированный доступ к данным без необходимости работать с массивами.
Каждый вызов datum() может перезаписывать ранее
установленное значение:
const svg = d3.select("svg");
svg.datum(100);
svg.append("g")
.datum(200)
.append("text")
.text(d => d);
Результат: текст будет содержать 200, так как дочерний
элемент переопределил значение родителя.
Если данные уже привязаны, повторный вызов datum()
заменяет их без участия join-логики.
const node = d3.select("#item");
node.datum({ id: 1, value: 10 });
node.datum({ id: 2, value: 20 });
После второго вызова старое значение полностью теряется.
datum() часто применяется как лёгкий механизм хранения
состояния внутри DOM-структуры.
d3.select("#toggle")
.datum({ active: false })
.on("click", function(event, d) {
d.active = !d.active;
d3.select(this).classed("active", d.active);
});
Здесь объект состояния хранится прямо в элементе и изменяется без внешних структур данных.
Если выборка содержит несколько элементов, одно и то же значение применяется ко всем:
d3.selectAll(".point")
.datum({ highlighted: true });
Каждый элемент .point получает одинаковый объект. Важно
учитывать, что это одна и та же ссылка, а не копия.
При передаче объектов через datum() сохраняется ссылка,
а не клонированное значение:
const state = { value: 1 };
d3.selectAll("circle")
.datum(state);
state.value = 999;
Все элементы будут видеть обновлённое значение 999, так
как используется общий объект.
datum() может применяться для простых обновлений без
data join:
function updateLabel(value) {
d3.select("#label")
.datum(value)
.text(d => `Value: ${d}`);
}
Каждый вызов функции заменяет данные и обновляет отображение.
Метод хорошо комбинируется с переходами и модификациями атрибутов:
d3.select("circle")
.datum({ r: 10 })
.transition()
.duration(500)
.attr("r", d => d.r * 2);
Здесь значение используется как источник параметров анимации.
Использование datum() имеет ряд особенностей:
const chartConfig = {
width: 800,
height: 400,
margin: 20
};
const svg = d3.select("svg")
.datum(chartConfig);
svg.append("g")
.attr("transform", `translate(${chartConfig.margin},${chartConfig.margin})`)
.append("text")
.text(d => `Размер: ${d.width}×${d.height}`);
Здесь datum() используется как контейнер конфигурации
для всего графика.
d3.selectAll("rect")
.datum(function(_, i) {
return { index: i, selected: false };
})
.on("click", function(event, d) {
d.selected = !d.selected;
d3.select(this)
.attr("opacity", d.selected ? 1 : 0.5);
});
Каждый элемент получает собственный объект состояния без необходимости data join.
При повторном вызове datum() данные полностью
заменяются:
const el = d3.select("#node");
el.datum({ a: 1 });
el.datum({ b: 2 });
После второго вызова информация { a: 1 } недоступна.
.text() и
.attr()Хотя datum() не изменяет визуальное представление
напрямую, он становится источником данных для всех последующих
операций:
d3.select("text")
.datum(5)
.text(d => d * 10);
Значение используется как промежуточный слой между логикой и отображением.
Часто возникающие проблемы связаны с неправильным пониманием ссылочной модели:
datum() как замену
data() для массивов// все элементы разделяют один объект
d3.selectAll("circle")
.datum({ active: false });
Хотя методы различаются, они могут использоваться совместно:
const dataset = [1, 2, 3];
d3.select("svg")
.datum({ scale: 2 })
.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("r", d => d * 5);
Здесь datum() задаёт глобальный контекст, а
data() — локальные значения элементов.
Если вызвать datum() на пустой выборке, результатом
будет undefined, так как данных нет.
d3.select(null).datum(); // undefined