Иън Джонсън, @enjalot, създава Building Blocksпросто уеб приложение за редактиране на примерни кодове, които са съвместими с «http://bl.ocks.org, което е де факто средата за споделяне на d3. js примери за код. Неговата кампания в kickstarter за проекта вече е финансирана два пъти и остават 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 елемент 32px надясно и 250px надолу, за да дадем някаква подложка и повече или по-малко вертикално да го центрираме.

// 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 също ще следи новите допълнителни индекси/данни за данните, които са получени, но все още няма съответни елементи за съхраняване на тези данни.

// 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, даваме му клас „enter“ и задаваме атрибутите 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 и мнозина намират модела за много неинтуитивен в началото, така че се надявам тази статия да изясни известно объркване! Чувствайте се свободни да оставите коментар или да се свържете с мен в twitter @QuintonAiken, ако имате въпроси.

Първоначално публикувано в quintonlouisaiken.com на 27 юли 2015 г.