Большая часть веб-разработки связана с манипулированием DOM. Процесс прост: получить ссылку на элементы DOM, а затем изменить их атрибуты.

// Plain Javascript
document.getElementById('element').style.fill ='blue';
// jQuery
$('#element').css('fill', 'blue');
// D3
d3.select('#element').style('fill', 'blue');

Но есть важное предположение, которое мы делаем здесь, не осознавая этого - мы предполагаем, что выбор элемента является операцией только для чтения. То есть простое получение ссылки на узел DOM не его модифицирует.

В D3 это не всегда так.

Рассмотрим следующую структуру DOM:

<svg width="50" height="50">
  <circle r="20" cx="25" cy="25"></circle>
</svg>

Затем попробуйте запустить этот код:

let svg = d3.select('svg'),
    circle = d3.select('circle');
// (1) Bind some data.
svg.datum({ color: 'blue' });
circle.datum({ color: 'red' });
// (2) Make the circle red.
circle.style('fill', d => d.color);
// (3) Does nothing, just selecting stuff.
svg.select('circle');
// (4) Same code as #2, now makes the circle... blue??? 😲
circle.style('fill', d => d.color);

Попробуйте закомментировать эту, казалось бы, бесполезную предпоследнюю строку, чтобы узнать, что здесь происходит. Действительно, выбор не доступен только для чтения. Похоже, что операция select заставляет элемент круга наследовать данные своего родителя.

Чтобы понять, почему было принято это дизайнерское решение и почему оно действительно полезно, мы сначала должны вспомнить самый важный коммерческий аргумент D3: соединение данных (в дальнейшем я предполагаю, что вы знакомы с этой парадигмой).

Рассмотрим следующее:

<ul>
  <li><span>1</span></li>
  <li><span>2</span></li>
  <li><span>3</span></li>
</ul>

Теперь давайте свяжем новые данные и войдем / обновим / выйдем как обычно:

let li = d3.select('ul')
  .selectAll('li')
  .data(['one', 'two', 'three']);
li.exit().remove();
li.select('span')
  .text(d => d);
li.enter().append('span')
    .text(d => d);

Что произойдет, если selection .select не распространит данные от родительского элемента? Нам понадобится другой метод получения данных из элемента li в элемент span, иначе дочерний элемент будет иметь старые данные (или вообще не иметь данных, если DOM был инициализирован статически). Мы также могли бы найти умный, но неудобный доступ к __data__.

Позволяя данным перемещаться по пути выбора, selection .select становится симметричным с selection .append, первый представляет часть данных update. -join, а последний представляет ввод.

D3 - это необычный способ думать о манипуляциях с DOM, а это означает, что в некоторых случаях требуется некоторое переобучение нашей ментальной модели. И когда мы преодолеваем эту горку, мы получаем в награду мощный, тщательно продуманный инструмент для связывания данных с визуальными элементами.

Эту концепцию мне объяснил Майк Босток, создатель и главный сопровождающий D3, после того, как я фактически зарегистрировал ее как ошибку 😬 .