Понимание того, как D3.js связывает данные с узлами

Я читаю документацию D3.js, и мне трудно понять метод selection.data из документации.

Это пример кода, приведенного в документации:

var matrix = [
  [11975,  5871, 8916, 2868],
  [ 1951, 10048, 2060, 6171],
  [ 8010, 16145, 8090, 8045],
  [ 1013,   990,  940, 6907]
];

var tr = d3.select("body").append("table").selectAll("tr")
    .data(matrix)
  .enter().append("tr");

var td = tr.selectAll("td")
    .data(function(d) { return d; })
  .enter().append("td")
    .text(function(d) { return d; });

Я понимаю большую часть этого, но что происходит с секцией .data(function(d) { return d; }) оператора var td?

Мое лучшее предположение заключается в следующем:

  • Оператор var tr привязал четырехэлементный массив к каждому tr node
  • Затем оператор var td использует этот четырехэлементный массив в качестве своих данных, как-то

Но как .data(function(d) { return d; }) действительно получает эти данные и что он возвращает?

Ответ 1

Когда вы пишете:

….data(someArray).enter().append('foo');

D3 создает кучу элементов <foo>, по одному для каждой записи в массиве. Что еще более важно, он также связывает данные для каждой записи в массиве с этим элементом DOM как свойство __data__.

Попробуйте следующее:

var data = [ {msg:"Hello",cats:42}, {msg:"World",cats:17} ]; 
d3.select("body").selectAll("q").data(data).enter().append("q");
console.log( document.querySelector('q').__data__ );

То, что вы увидите (в консоли), является объектом {msg:"Hello",cats:42}, так как это было связано с первым созданным элементом q.

Если вы позже выполните:

d3.selectAll('q').data(function(d){
  // stuff
});

значение d оказывается тем свойством __data__. (В этот момент вам нужно заменить // stuff кодом, который возвращает новый массив значений.)

Вот еще один пример, показывающий данные, связанные с элементом HTML, и возможность повторного связывания подмножеств данных с нижними элементами:

  no description

Ответ 2

Ключом к пониманию того, что делает этот код, является распознавание того, что выбраны массивы массивов элементов DOM. Самый внешний массив называется "выбором", внутренний массив называется "группами", и эти группы содержат элементы DOM. Вы можете проверить это, зайдя в консоль на d3js.org и сделав выбор, например d3.selectAll('p'), вы увидите массив, содержащий массив, содержащий элементы "p".

В вашем примере, когда вы сначала вызываете selectAll ('tr'), вы получаете выделение с одной группой, содержащей все элементы tr. Затем каждый элемент из matrix сопоставляется каждому элементу tr.

Но когда вы вызываете selectAll ('td') в этом выборе, выбор уже содержит группу элементов 'tr'. На этот раз каждый из этих элементов будет становиться группой элементов 'td'. Группа - это всего лишь массив, но также имеет свойство parentNode, которое ссылается на старый выбор, в данном случае на элементы tr.

Теперь, когда вы вызываете data(function(d) { return d; }) в этом новом выборе элементов 'td', d представляет данные, привязанные к каждой родительской группе node. Таким образом, в примере "td в первой группе будет связан с массивом [11975, 5871, 8916, 2868]. Вторая группа" td связана с [1951, 10048, 2060, 6171].

Вы можете прочитать mike bostock собственное отличное объяснение выбора и привязки данных здесь: http://bost.ocks.org/mike/selection/

Ответ 3

Используйте счетчик i, чтобы показать индекс используемых данных.

var tr = d3.select("body").append("table").selectAll("tr")
.data(matrix)
.enter().append("tr") //create a row for each data entry, first index
.text(function(d, i) { return i}); // show the index i.e. d[0][] then d[1][] etc.

var td = tr.selectAll("td")
.data(function(d) { return d; })
.enter().append("td")
.style("background-color", "yellow") //show each cell
.text(function(d,i) { return i + " " + d; }); // i.e d[from the tr][0] then d[from the tr][1]...