Ян Джонсон, @enjalot, создает Building Blocks - простое веб-приложение для редактирования примеров кода, совместимых с« http://bl.ocks.org , который де-факто является средой для обмена d3. js примеры кода». Его кампания по кикстартеру для этого проекта уже дважды профинансирована, осталось 22 дня. Сейчас легко зайти на bl.ocks.org и быстро вдохновиться различными проектами. Однако напрямую играть с кодом труднее. Ян Джонсон собирается это изменить.

Building Blocks позволит многим пользователям, которые ранее не были знакомы с D3, легко запачкать руки с кодом. Точно так же это позволит более опытным разработчикам D3 настраивать параметры, чтобы получить более глубокое понимание того, что на самом деле происходит.

Чтобы отпраздновать это событие, я собираюсь обсудить «общую схему обновления» в D3. Это вводный шаблон, который является ключом к пониманию того, как данные привязаны к DOM в D3.

Код, который мы будем обсуждать, находится по адресу http://bl.ocks.org/mbostock/3808218. Давайте начнем.

var alphabet = "abcdefghijklmnopqrstuvwxyz".split("");
var width = 960;
var height = 500;
var svg = d3.select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height)
  .append("g")
  .attr("transform", "translate(32," + (height / 2) + ")");

Мы добавляем svg элемент с определенной шириной и высотой к телу. Затем мы добавляем элемент g к этому svg. Элемент g используется как контейнер для группировки других объектов. Мы также переводим этот элемент g на 32 пикселя вправо и на 250 пикселей вниз, чтобы добавить отступ и более или менее центрировать его по вертикали.

// The initial display.
update(alphabet);
setInterval(function() {
  update(
    d3
      .shuffle(alphabet)
      .slice(0, Math.floor(Math.random() * 26))
      .sort()
  );
}, 1500);

Сначала мы вызываем функцию обновления со всем алфавитом. Затем каждые 1,5 секунды мы вызываем функцию со случайной выборкой букв из алфавита в алфавитном порядке. Связывание данных происходит в функции обновления.

function update (data) {
  // DATA JOIN
  var text = svg
    .selectAll("text")
    .data(data);
  // "UPDATE SELECTION"
  text.attr("class", "update");
  // "ENTER SELECTION"
  text
    .enter()
    .append("text")
    .attr("class", "enter")
    .attr("x", function(d, i) { return i * 32; })
    .attr("dy", ".35em");
  // "ENTER + UPDATE SELECTION"
  // I.E., THE NEW UPDATE SELECTION
  text.text(function(d) { return d; });
  // "EXIT SELECTION"
  text.exit().remove();
}

За объединением данных следуют операции с выбором «входа, обновления и выхода». Давайте разберем каждый шаг.

// DATA JOIN
var text = svg
  .selectAll("text")
  .data(data);

Это выберет все text элементы в svg и присоединит к ним данные. По умолчанию данные прикрепляются по индексу, поэтому данные в data [0] будут прикреплены, например, к первому элементу text, тогда как данные в data [3] будут прикреплены к четвертому элементу text. Самым запутанным в этом шаблоне является то, что эти текстовые элементы могут существовать, а могут и не существовать.

В этом примере переменная text вернет объект с методами enter и exit. У него также будет свойство, указывающее на массив выбранных text элементов. Если text элементов не существует, этот массив будет пустым. Однако он по-прежнему будет той же длины, что и массив данных, который мы передали, и переданные данные по-прежнему связаны с каждым индексом.

Если все text элементы действительно существуют, элементы получат новые данные, снова соответствующие их индексам. Если некоторые text элементы существуют, но мы получили новый массив данных, длина которого больше, чем количество text элементов, имеющихся в настоящее время в DOM, то существующие text элементы получат новые данные, а наша переменная text также будет отслеживать новые дополнительные indexes / data для данных, которые были получены, но не имеют соответствующих элементов для размещения этих данных.

// UPDATE SELECTION
text.attr("class", "update");

После объединения данных наша переменная text сама является нашим «выбором обновления». Он соответствует уже существующим элементам в модели DOM, получившим новые данные. При первом запуске нашей функции обновления это ничего не даст, поскольку text элементов еще не существует.

// "ENTER SELECTION"
text
  .enter()
  .append("text")
  .attr("class", "enter")
  .attr("x", function(d, i) { return i * 32; })
  .attr("dy", ".35em");

Это наш «входной выбор». Он соответствует данным, которые были получены, но не имеют соответствующих элементов для размещения этих данных. Вы можете думать об этом как о том, что «для каждой части данных в переменной text, которая еще не имеет существующего элемента, выполните действия после вызова enter ()».

При первом вызове функции обновления она будет выполняться для каждого фрагмента вставленных данных. Мы добавляем новый элемент text к нашему элементу g, присваиваем ему класс «ввод» и устанавливаем атрибуты x и y для каждой части данных.

// "ENTER + UPDATE SELECTION"
// I.E., THE NEW UPDATE SELECTION
text.text(function(d) { return d; });

Опять же, наша переменная text соответствует нашему «выбору обновления». После того, как мы выполнили наши операции «ввода», переменная обновится, чтобы включить все вновь вставленные text элементы. Это потому, что наш «выбор обновления» просто соответствует существующим элементам в модели DOM, которые получили новые данные.

// "EXIT SELECTION"
text.exit().remove();

Это наш «выбор выхода». Это соответствует всем существующим элементам DOM в нашей выборке, для которых не было найдено новых фрагментов данных. Часто возникает желание «удалить» элементы «выбора выхода» из модели DOM. При первом прохождении этот выбор будет пустым.

Чтобы собрать все это вместе, предположим, что у нас есть 5 text элементов в нашем svg с данными «a», «b», «c», «d», «e» соответственно. Затем мы вызываем нашу функцию обновления с другим циклом данных - «j», «k», «l».

Наше «соединение данных» соединит «j» с нашим первым text элементом, «k» - со вторым, а «l» - с третьим. «D» по-прежнему будет ассоциироваться с четвертым элементом, а «e» по-прежнему будет ассоциироваться с последним элементом.

Наш «выбор обновления» будет соответствовать только элементам, получившим новые данные - первым трем элементам.

У нас не будет «выбора для ввода», потому что у нас есть элементы для всех наших новых частей данных. Нам не нужно добавлять какие-либо новые элементы для представления наших данных.

Наш «выбор выхода» будет соответствовать нашим последним двум элементам - элементам, связанным с «d» и «e». Для этих элементов не найдено новых данных, поэтому мы их удаляем.

В нашей следующей итерации, допустим, мы вызываем нашу функцию обновления с массивом значений - «f», «i», «m», «p», «q».

Мы снова привязываем наши новые данные к нашим существующим text элементам. Однако у нас есть 5 частей данных и в настоящее время только 3 text элемента. Эти 3 элемента будут соответствовать «f», «i» и «m» соответственно. См. Http://bl.ocks.org/mbostock/3808221 для примера того, как добавить ключ к «объединению данных», чтобы переопределить привязку по умолчанию «по индексу».

Наш «выбор обновления» снова соответствует только существующим элементам, получившим новые данные - первым трем элементам.

Наш «выбор ввода» соответствует частям данных «p» и «q» - данным, которые не содержат текущих элементов для их размещения. Таким образом, мы добавляем новый элемент text для каждого из них.

Наш «выбор выхода» будет пустым, потому что были новые данные для всех наших существующих в настоящее время элементов.

Четкое понимание общего шаблона обновления является ключевым в D3, и многие находят этот шаблон поначалу очень нелогичным, поэтому я надеюсь, что эта статья проясняет некоторую путаницу! Не стесняйтесь оставлять комментарии или обращаться ко мне в твиттере @QuintonAiken, если у вас есть какие-либо вопросы.

Первоначально опубликовано на сайте quintonlouisaiken.com 27 июля 2015 г.