Как стать уверенным в себе пользователем

d3.js - фантастическая библиотека, но я могу честно сказать вам, что долгое время я использовал ее, не понимая ее полностью. Не имея какой-либо твердой ментальной модели того, с чем я работал, я, по общему признанию, склонен копировать / вставлять фрагменты кода из различных фрагментов на bl.ocks.org, скрещивать пальцы и надеяться на лучшее.

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

Документация по API пугающе огромна, но есть ее подмножество, которое закладывает решающую основу для чтения и написания программ, использующих d3. Мое намерение здесь состоит в том, чтобы сообщить об этих основах таким образом, чтобы я хотел, чтобы они сообщались мне, когда я только начинал, и чтобы вы использовали его уверенно, а не неуверенно.

Давай сделаем это!

Существительные и глаголы

Всякий раз, когда я пытаюсь понять систему, я часто спрашиваю себя: «Что эта система стремится сделать, и что такое существительные и какие глаголы что я могу использовать, чтобы описать это на простом английском языке »? Если у вас был опыт работы с d3 раньше, вы бы знали, что типичная программа выглядит примерно так:

См. Здесь, чтобы увидеть, к чему приводит этот код. В ближайшее время он не получит никаких наград dataviz, но цель состоит в том, чтобы его урезать в достаточной степени, чтобы вести это обсуждение. Код может иметь разный смысл в зависимости от вашего опыта - если вы новичок в d3, он может вообще не иметь смысла.

Я надеюсь, что, проанализировав этот блок кода с точки зрения существительных и глаголов, что делает неявное выражение более явным, он будет иметь больше смысла для всех, кто имеет базовое понимание Javascript и SVG. Но есть вопрос, к которому мы должны обратиться в первую очередь - какова цель? Чего пытается достичь приведенный выше код?

Цель * Задача'

D3 означает «документы, управляемые данными», и поэтому целью является создание документа, основанного на данных (да). Я полагаю, что это цель всей визуализации данных - установить соответствие между некоторыми данными и некоторыми презентационными элементами, которые можно увидеть и, возможно, с ними взаимодействовать. Если бы данные изменились, мы бы ожидали, что изменится какой-то аспект визуального восприятия - это сопоставление.

Когда дело доходит до веб-документов, «презентационные элементы» имеют форму узлов. Если вы забыли, что такое узел, откройте инструменты разработчика и запустите document.querySelectorAll ('div'), чтобы увидеть все узлы ‹div /›, которые составляют эта текущая страница.

d3 выполняет философию dataviz отображения данных на узлы, принимая (возможно, очевидный) подход к хранению точек данных как свойств на самих узлах. Чтобы показать это, попробуйте открыть эту ссылку и запустить document.querySelectorAll (‘rect’) в инструментах разработчика. Вы должны увидеть массив из 10 ‹rect› узлов (по одному на каждый из элементов массива data, представленных в коде). Если вы развернете один из них, чтобы увидеть его атрибуты, вы должны увидеть все различные атрибуты, типичные для узла SVG. Но есть важное дополнение. Внизу находится свойство __data__:

Для этого конкретного ‹rect›, первого в списке, свойство принимает значение 3, которое также оказывается первым значением в наших данных. массив. Здесь мы подходим к первому «принципу» d3, что большая часть вашей работы будет включать привязку данных к узлам в форме атрибута __data__ на самом узле. Возможно, вы не подозреваете, что именно этим занимаетесь, поскольку API d3 скрывает от вас такие детали. Но «под капотом» это то, что вы делаете.

Большая часть вашей работы в d3 (непреднамеренно) будет включать привязку данных к узлам в форме атрибута __data__ на самом узле

После того, как данные успешно привязаны к узлу, становится возможным стилизовать узел на основе этих данных. Каждый раз, когда вы видите .attr (ключ, значение) в приведенном выше коде, рассматривайте это как попытку изменить внешний вид узлов. Например, вызов .attr («height», d = ›yScale (d)) присваивает свойству 'height' не фиксированное значение, а а значение, которое функция __data__, привязанная к этому узлу. Наши узлы зависят от данных в том смысле, что у нас есть набор узлов, по одному для каждой точки данных, с высотой, соответствующей значению этой точки данных. Другими словами, у нас есть «документ, управляемый данными».

Другой смысл, в котором документ «управляется данными», заключается в том, что мы, кажется, создали узел ‹rect› для каждого элемента в массиве data. Если бы в массиве было всего 7 элементов, программа создала бы только 7 ‹rect› узлов. Если бы в массиве было 12 элементов, программа создала бы 12 ‹rect› узлов и так далее. Учитывая, что мы стремились настроить сопоставление между точками данных и узлами ‹rect›, мы хотим убедиться, что у нас есть такое же количество точек данных, как у ‹rect› узлы.

Представьте, что по какой-то причине на момент запуска программы d3 в документе уже было 50 ‹rect› узлов. Учитывая, что мы стремимся сопоставить узел ‹rect› с каждой из наших 10 точек данных, это означает, что 40 из этих существующих в настоящее время узлов теперь являются избыточными, не так ли? Разве нет смысла удалять эти 40 узлов, поскольку они больше не отражают состояние наших данных?

Такой случай произошел бы, если бы мы запускали нашу программу несколько раз с разным количеством точек данных. Возможно, наша программа подключена к некоторым «живым данным», так что длина массива data изменяется со временем. Наша программа должна каким-то образом гарантировать, что количество узлов ‹rect› на странице соответствует длине массива данных. Если вы иногда находите D3 излишне сложным и трудным для понимания, имейте в виду, что он предназначен для обработки такого варианта использования. Это также то, что делает d3 таким мощным - мы не просто стремимся настроить сопоставление, мы стремимся настроить динамическое сопоставление, которое может надлежащим образом реагировать на изменения в данных.

API d3 разработан, чтобы позволить вам настроить динамическое сопоставление между данными и визуальными элементами, которое может соответствующим образом реагировать на изменения данных с течением времени.

Следовательно, в дополнение к назначению точек данных узлам, возможность создавать и удалять сами узлы, когда и когда это необходимо, является еще одним вариантом использования, для которого разработан d3.

Теперь, когда у нас есть общие цели (1) создание / удаление узлов на основе данных (2) присвоение значений данных узлам и (3) стилизуя узлы на основе этих значений данных, давайте вернемся к существительным и глаголам.

«Три цели» d3:

1. создание / удаление узлов на основе данных

2. присвоение значений данных узлам

3. стилизация узлов на основе этих значений данных.

Существительные + Глаголы

Нам нужно добавить несколько новых терминов в наш словарь, если мы собираемся разобраться в Первом фрагменте кода.

1: Selection (имя существительное)

Во-первых, я хочу, чтобы вы использовали термин выбор.

Когда вы смотрите на любой код d3, первое, что вы можете заметить, это то, что происходит много цепочек. Подобные цепочки возможны в Javascript всякий раз, когда возвращаемое значение метода имеет собственные методы. Например, вы могли видеть такие цепочки обещаний:

… Которые возможны, потому что каждый вызов .then сам возвращает обещание, у которого есть свой собственный метод .then, который можно вызвать, и так далее. В этом коде подразумевается, что «обещания раздаются».

Возникает вопрос, что «передается» в цепочке утверждений d3? В большинстве случаев ответ - выбор. Вы можете начать с того, что подумайте о выборе, как о результате вызова document.querySelectorAll () в том смысле, что он представляет собой набор узлов. Но, как мы увидим, это немного более сложная структура данных, чем простой массив.

Цепочка выбора обычно начинается с вызова d3.select или d3.selectAll. Например, вы можете вернуться к этой странице и вызвать d3.selectAll ('rect') в инструментах разработчика, точно так же, как вы вызвали собственный document.querySelectorAll ('rect' ) раньше. Если вы исследуете получившуюся структуру данных, вы увидите, что список узлов можно найти в свойстве _groups. Пока не зацикливайтесь на этой структуре данных, просто рассматривайте ее как набор узлов, предоставляющий удобный набор методов для управления всеми этими узлами за один вызов.

Подумайте о «выборе» как о наборе узлов, который предоставляет удобный набор методов для управления всеми этими узлами за один вызов.

Получив отбор, мы находимся в «d3-land». Для выбора доступно множество методов, которые при вызове могут сами возвращать выбор. например посмотрите этот раздел из фрагмента кода 1:

Цепочка выборок запускается вызовом d3.select (), который дает нам выбор, среди методов которого есть .style, .append и .attr, и когда любой из них вызывается, он возвращает выделение, которое само может вызывать .style, .append и .attr (среди прочего). Это объясняет, почему цепочки так распространены в d3. Подобно тому, как у обещаний есть методы, которые могут возвращать обещания, у выборок есть методы, которые могут возвращать выборки.

Вызывая метод для выборки, вы, по сути, говорите «сделайте что-нибудь для каждого узла в этой выборке». Среди методов, доступных для выбора, есть метод .select. Это потенциально сбивает с толку (это было для меня). Каждый раз, когда вы видите вызов .select () в каком-либо коде d3, стоит намеренно отметить, относится ли он к d3.select или относится к Метод .select вызывается для существующего выделения (т. е. selection.select). В первом случае мы выбираем узел. В последнем случае мы выбираем узел-потомок для каждого узла в текущем выборе, чтобы узлы в текущем выделении «отображались» 1: 1 в набор новых единицы. Я хотел подчеркнуть это различие сейчас, потому что в какой-то момент вы столкнетесь с этим.

Ключевой частью понимания того, что строка кода d3 делает в определенный момент времени, является вопрос себе каково текущее состояние выбора? Можете ли вы выяснить какие узлы были выбраны и над которыми в настоящее время работают? Давайте проделаем это упражнение с фрагментом кода 2.

  1. Во-первых, у нас есть d3.select (‘body’). Это дает нам выбор, который содержит единственный узел, которым оказывается узел ‹body›, который уже присутствует в документе до того, как код javascript начнет свое выполнение.
  2. Затем мы вызываем .style («text-align», «center»), который служит для изменения презентационного аспекта этого узла. Если в этот момент в выборке оказалось более одного узла, каждый узел был бы мутирован таким образом. Результат - тот же выбор, что и раньше (единственный узел ‹body›)
  3. Затем мы вызываем .append («svg»). Это также изменяет узел ‹body› в том смысле, что добавляет к нему узел ‹svg› как новый дочерний элемент. Однако на этот раз вместо того, чтобы вернуть исходный выбор (тот, что был в строке 1), он возвращает новый выбор, состоящий из только что добавленного узла ‹svg›. Фактически selection.append служит для добавления нового узла, а затем вызывает selection.select для его выбора (это «оболочка» вокруг selection.select )
  4. Следующие три вызова методов: .style («граница», «сплошной черный 1 пиксель»), .attr («ширина», 200) и .attr ( «Высота», высота) все служат для стилистического изменения узлов в текущем выделении (помните, что в этом случае он в настоящее время состоит из одного узла ‹svg›) и возвращают тот же выбор, на который он действовал.

Поэтому вам должно быть очевидно, что методы, доступные для выбора, можно разделить на те, которые служат для мутации или изменения внешнего вида узлов в текущем выборе (например, selection.attr), и методы, которые могут обновить то, к чему относится текущий выбор (например, selection.select), и те, которые делают и то, и другое (например, selection.append). (Я подумывал использовать термины косметические обновления и выборочные обновления как способ закрепить различие между этими случаями; возможно, вы сами сможете придумать несколько лучших.)

Это, на мой взгляд, также объясняет, почему у d3 такая крутая кривая обучения. Некоторые методы, такие как selection.append, могут служить для «незаметного» обновления того, к чему относится текущий выбор, и, если вы этого не ожидаете, вы можете быть неприятно удивлены. При изучении новых методов, которые делает доступным вам выбор, стоит потратить время, чтобы спросить себя, служит ли он для обновления выбора (то есть относится ли выбор к тому же набору узлов, что и раньше) или его эффект ограничен сами узлы.

Итак, к настоящему моменту вы должны были понять, что создание выбора - это способ D3 ссылаться на наборы узлов, так что после того, как выбор сделан, можно манипулировать стилистическими атрибутами узлов, которые он содержит. Второй фрагмент кода можно описать простым английским языком как «выберите основной узел и обновите его стиль».

Далее я хочу представить третий фрагмент кода:

На первый взгляд, это может показаться похожим на Второй фрагмент кода, но на самом деле это требует, чтобы вы добавили в свой словарь еще несколько существительных, чтобы полностью понять его.

Давайте посмотрим на первый бит, svg.selectAll (‘rect’):

svg - это ярлык, который мы дали выделенному фрагменту, который является результатом последней строки второго блока кода (не думайте, что это какая-то глобальная переменная). Другими словами, это выделенный фрагмент, содержащий узел ‹svg›, который мы добавили к узлу ‹body›.

Помните, как мы подчеркивали, что selection.select ('x') отличается от d3.select ('x') и означает, что 'для каждого узла в этом выделении, выбрать первый узел-потомок, соответствующий x '? Итак, selection.selectAll можно примерно интерпретировать как «для каждого узла в этом текущем выделении выберите каждый дочерний узел, соответствующий x». Следовательно, svg.selectAll (‘rect’) можно интерпретировать как означающий «выбор каждого элемента rect, который происходит от нашего узла ‹svg›».

Подведем итог этим различиям:

d3.select (x) = выделите мне выделение, которое содержит первый узел в документе, соответствующий x
d3.selectAll (x) = выделите мне выбор, содержащий все узлы в документе, соответствующие x
selection.select (x) = для каждого узла в текущем выделении укажите первый дочерний узел, который соответствует x (сопоставление 1: 1)
selection.selectAll (x) = для каждого узла в текущем выделении, дайте мне все дочерние узлы, соответствующие x (сопоставление 1: многие)

Итак, первая часть третьего фрагмента кода служит для «выбора каждого элемента rect, который происходит от нашего узла ‹svg›». Здесь проницательный читатель может задаться вопросом:

«Но нет никаких элементов rect? Пока у нас есть только основной узел с добавленным к нему узлом ‹svg›? »

Если предположить, что этот код запускается впервые на пустом документе, такой ответ будет правильным. Выделение, полученное в результате вызова svg.selectAll (‘rect’), будет пустым выделением.

Итак, теперь у нас есть пустой выбор, и следующий шаг вызывает для него .data (data). Что это за операция? Обновляет ли он выбор или изменяет узлы? Ответ вроде и того, и другого. Если бы в текущем выборе были на самом деле некоторые узлы, он обновил бы их в том смысле, что он служит для назначения i -й точки данных как __ data__ на i -м узле в текущем выделении '. В этом смысле он достигнет второй «широкой цели» d3, а именно присвоения значений данных узлам.

В некотором смысле это также «выборочное обновление». Это все тот же выбор в том смысле, что это все еще пустой выбор. И если бы мы вызвали .data (data) для выборки из 10 ‹rect› узлов, то у нас все равно останется выбор из 10 ‹rect › узлы. Но кое-что изменилось. У нас остался тип выбора, который, как я считаю, заслуживает своего собственного существительного. Каждый раз, когда мы вызываем .data () для выделения, это приводит к привязке.

2: Binding (имя существительное)

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

  • Посетите эту ссылку (это то же самое, что и первый фрагмент кода, только я добавил пару журналов консоли, чтобы проверить характер выбора до и после вызова .data ())
  • Откройте инструменты разработчика, чтобы просмотреть журналы.
  • Изучите различия между двумя операторами журнала

Первое, что вы должны заметить, это то, что у выделения теперь есть два дополнительных ключа: _enter и _exit. Итак, я думаю, мы можем согласиться с тем, что, хотя мы все еще занимаемся отбором, это уже не совсем тот отбор, который был раньше. Теперь это привязка.

К чему относятся _enter и _exit? Помните первую «цель» d3? Это было создание / удаление узлов на основе данных.

  • Когда мы впервые запускаем программу d3 в пустом документе, обычно у нас меньше узлов, чем точек данных, поэтому, если мы хотим сопоставить узлы с точками данных, мы должны их создать.
  • Если это не первый раз, когда мы запускаем программу в документе (если, например, наша программа подключена к некоторым «живым» данным, которые регулярно обновляются), и у нас меньше точек данных, чем раньше, то это, вероятно, будет означать, что теперь у нас в документе больше узлов, чем нам нужно. В этом случае необходимо будет удалить некоторые из уже созданных ранее избыточных узлов.

Давайте сосредоточимся на конкретном случае, в котором мы стремимся сопоставить каждую точку данных в нашем массиве data с узлом ‹rect› в документе. Нам нужен способ представить каждый из трех случаев (при условии, что массив data может изменяться со временем):

  1. Узел, который нам нужно создать, потому что конкретная точка данных еще не сопоставлена ​​с узлом в документе.
  2. Узел, который нам нужно удалить, потому что его связанная точка данных была удалена, и вместе с ним должен идти связанный узел
  3. Узел, который нам нужно обновить, так как связанная с ним точка данных была обновлена.

После передачи нашего массива данных (он всегда должен быть типом array) к выбранным узлам, которые мы хотим сопоставить с ним, перейдя selection.data (data) , d3 имеет всю информацию, необходимую для сортировки текущего выбора узла по трем вышеупомянутым случаям. d3 использует термины ввод, выход и обновить для обозначения этих трех случаев соответственно. Поскольку выбор - это термин d3 для обозначения набора узлов, мы называем набор узлов, подпадающих под случай 1, выбором ввода, набором узлов, которые выпадают. в случае 2 как выбор выхода, а набор узлов, подпадающих под случай 3, как выбор обновления. Вот диаграмма, которую я беззастенчиво взял прямо с Майка Бостока, создателя сайта d3:

3. «Псевдо-узлы» (существительное)

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

На этом стоит остановиться на секунду, потому что он дает самое непосредственное отношение к тому, что людям часто бывает трудно понять, когда они изучают d3. «Совокупность узлов, которые еще не существуют» - это сложная задача для понимания. Если вы думаете об этом как о «наборе псевдоузлов», это может помочь. В любом случае на этом этапе нам необходимо обновить определение выбора как «совокупности узлов, независимо от того, существуют ли они на самом деле в документе или нет».

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

Итак, давайте рассмотрим, где мы находимся. Вызывая svg.selectAll («rect»). Data (data) в первый раз, мы получаем привязку со ссылками на:

  • выбор ввода, который состоит из 10 еще не созданных узлов, соответствующих 10 элементам данных. Тип этих узлов (т. Е. Являются ли они ‹rect› s или являются чем-то другим, например ‹circle› s) еще не указан. Этот выбор получается путем вызова .enter ().
  • пустой выбор выхода. В документе не было узлов ‹rect›, с которых можно было бы начинать - svg. selectAll ('rect') вернул пустой выбор, поэтому не было вероятность этого выбора, включая любые избыточные узлы ‹rect›. Этот выбор получается путем вызова .exit ().
  • выбор обновления, который пуст. Опять же, поскольку в документе не было узлов ‹rect› для начала, нет ни одного, требующего обновления с новыми данными. Этот выбор возвращается вам по умолчанию - вам не нужно вызывать какие-либо дополнительные методы для его выбора.

Таким образом, привязка - это просто выбор, состоящий из наших узлов обновления и содержащий некоторые специальные методы, которые дают нам доступ к вариантам ввода и выхода. . Следовательно, вы можете встретить такие шаблоны:

На простом английском языке это можно интерпретировать как означающее: «выберите любые существующие узлы ‹rect›, которые происходят от нашего узла ‹svg›, и установите __data __ атрибута i -го прямоугольного элемента с i -й точкой данных в data. Для любой точки данных, не имеющей связанного узла, создайте новый элемент ‹rect›, представляющий ее ». Давайте кратко остановимся на этом.

Ранее мы узнали, что selection.append служит как для изменения узлов в выделении (путем добавления к ним нового узла), так и для возврата вновь добавленных узлов в виде нового нового выделения. В этом случае вызов. append ('rect') говорит: «для каждого из псевдоузлов в нашем выборе ввода добавьте ‹rect› и верните эта свежая новая подборка ‹rect› s ”.

4. Размножать (глагол)

В предыдущем примере узел i th ‹rect› в конечном итоге сохранит i -ю точку данных в качестве своей Атрибут __data__. Этот результат не совсем очевиден и заслуживает особого внимания. Мы должны расширить наше определение selection.select как «для каждого узла в этом выборе выберите первый дочерний узел, соответствующий x , и распространите на него данные».

Как мы упоминали ранее, метод .append на самом деле является оболочкой для. select,, и поэтому .append также имеет это свойство. В приведенном выше случае он служит для создания узла ‹rect› для каждого из наших псевдоузлов (каждый из которых, если вы помните, имел привязку __data__), распространить точки данных по вновь созданным «реальным» узлам в режиме 1: 1, а затем вернуть эти вновь созданные узлы в новом выборе. (обратите внимание, что selection.selectAll не имеет этого свойства распространения данных - возможно, это связано с тем, что нет такого сопоставления 1: 1 с вновь выбранными узлами, и поэтому он непросто сказать, какие данные куда должны идти).

selection.select имеет побочный эффект распространения данных на вновь выбранные узлы, так что они имеют тот же атрибут __data__, что и узел, из которого они были выбраны. selection.selectAll НЕ имеет этого свойства

Любые дополнительные вызовы методов, которые могут быть связаны в конце, будут служить для работы с этими новыми узлами ‹rect›. Может быть, вы захотите дать им размер, положение или цвет. В приведенном ниже случае мы дадим им высоту, зависящую от их __data__:

Если вызов svg.selectAll ('rect') вернул некоторые узлы (т. Е. Не пустой выбор), то последующий вызов .data (data) обновил бы некоторые из них новыми данными. Я имею в виду, что их атрибут __data__ был бы переназначен на какое-то новое значение. Но это будет незаметно для пользователя, если мы не выполним косметическое преобразование на основе данных для этих узлов.

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

Большинство примеров d3, которые вы увидите в Интернете, как правило, ссылаются только на выбор ввода. Если вы намерены создать визуальное представление некоторых статических данных (т.е. предполагая, что массив данных никогда не изменится), тогда что-нибудь вроде enterWithStyling.js (фрагмент кода двумя блоками выше) , который работает только с выбором enter, будет достаточно. Работая с вариантами обновить и выйти, вы также настраиваете динамическое сопоставление, которое будет реагировать на обновления данных.

Существует как минимум два способа объединения enterWithStyling.js и visibleUpdate.js (т. Е. Для создания и стилизации узлов ‹rect› из scratch 'и обновлять их при изменении данных). «Старый» способ сделать это:

Здесь мы сохраняем ссылку на привязку, которая, по сути, является выбором обновления, и из нее получаем выбор ввода (который мы используем для создания ‹ прямоугольные › узлы с нуля), а затем объединить его (подумайте об этом как об объединении наборов, если вы математически склонны) обратно с выбором обновления. Если мы спросим себя, «к чему относится выделение» после операции «слияния», мы можем сказать, что это относится к «вновь созданным прямоугольным узлам вместе с узлами, которые уже были там изначально».

Теперь, когда выбор относится к этой комбинации случаев, мы можем стилизовать этот выбор узлов одним махом, используя selection.attr. См. Здесь, чтобы увидеть пример того, как это происходит (я установил динамический набор данных, чтобы показать ценность таких действий).

Вы могли заметить, что в коде нет ссылки на выбор выхода. Действительно, если вы посмотрите на пример, то увидите, что со временем количество узлов ‹rect› на странице никогда не уменьшается, хотя иногда и должно, в случаях, когда массив данных укорачивается на длина. Давайте исправим это, обратившись к выбору exit:

Для каждого выбора доступен метод .remove, который служит для удаления всех содержащихся узлов. (Для текущих целей трудно понять, почему нужно удалить какие-либо узлы, кроме выбора exit). Посмотрим, как это выглядит.

Хорошо, так лучше. Теперь мы можем видеть, как количество узлов ‹rect› на странице увеличивается и уменьшается вместе с длиной массива данных. Наконец-то у нас есть «динамическое отображение».

В более новых версиях d3 представлен новый метод выбора, называемый selection.join, который значительно упрощает весь этот процесс и с меньшим количеством «шаблонов». Вы можете узнать этот метод из первого фрагмента кода. С его помощью мы можем переписать предыдущий пример следующим образом:

selection.join добавит указанный элемент к каждому из «псевдоузлов» в выборе enter и вернет вам эти новые узлы и обновленные узлы (если any) в одном выделении, чтобы последующие строки можно было использовать для стилизации обоих одновременно. Он также удалит все узлы, которые стали избыточными из-за последней привязки данных.

Подведение итогов

Если вы зашли так далеко, значит, у вас есть все ментальные модели и терминология, необходимые для понимания первого фрагмента кода. Единственное, что я еще не рассмотрел, - это yScale, который находится в верхней части фрагмента кода, но я предполагаю, что вы уже знаете, что такое шкала. В d3 для создания шкалы необходимо написать следующий «скелет»:

генератор (). домен (). диапазон ()

И вы получаете функцию, которая отображает значения в вашем наборе данных в выходной диапазон, который обычно ссылается на координаты SVG. Стоит отметить, что в координатах SVG значение y увеличивается при перемещении вниз по странице. Схема ниже должна помочь объяснить, почему нам нужно установить координату y каждого прямоугольника в соответствии с height - yScale (d) (координата y относится к положению верхнего края узла ‹rect›):

Само собой разумеется, что d3 также предоставляет множество подобных «вспомогательных» функций, которые значительно упрощают построение всевозможных типов диаграмм. Удачи, исследуя их все!

Этого должно быть достаточно, чтобы вы начали. Следующий уровень будет заключаться в том, чтобы придти к более совершенной ментальной модели того, что на самом деле представляет собой выбор. Мне все еще нужно познакомить вас с другим существительным, которое называется группа. Это поможет вам лучше понять процесс привязки данных. Если вам не терпится узнать об этом, прочтите эту статью.

Если этот учебник был вам полезен, сообщите мне об этом в комментариях, и я напишу еще одну статью, в которой будет более подробно объясняться выбор и, возможно, устранены утечки абстракций. Кроме того, мне было бы интересно узнать, что вы думаете об этом способе структурирования статьи с практическими рекомендациями с точки зрения существительных и глаголов. Было ли это полезно?

'До следующего раза.