любопытство к методу selectAll в d3 - PullRequest
0 голосов
/ 23 июня 2018

Я пытаюсь понять, как работают методы d3.Я думаю, что я довольно хорошо получаю d3, базовый уровень, конечно, но есть одна странная вещь в методе selectAll, которого я не понимаю.Поэтому, когда я пытаюсь создать и добавить узлы dom к выбранному элементу dom, независимо от того, существует он или нет, иногда он создает четыре узла, два или шесть в других случаях.Чтобы прояснить вопрос, я собираюсь использовать простые примеры.

HTML:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
  </head>
  <body>
     <svg></svg>
  </body>
</html>

JS:

    dummyData = [
         {
           name: 'A',
           age: 50
         },
         {
           name: 'B',
           age: 20
         }
       ]

    svg = d3.select('svg')
            .attr('width','500')
            .attr('height', '300')
          .append('g')
            .attr('transform','translate(40, 40)');


function example_1() {

    svg.selectAll('circle')
      .data(dummyData)
      .enter()
    .append('circle')
      .attr('transform', d => `translate(${d.age}, 20)`)
      .attr('cx',32).attr('cy',53)
      .attr('r',15);

 }


function example_2() {

    svg.selectAll('g')
      .data(dummyData)
      .enter()
    .append('circle')
      .attr('transform', d => `translate(${d.age}, 20)`)
      .attr('cx',32)
      .attr('cy',53)
      .attr('r',15);
}

function example_3() {

    svg.selectAll('div')  // This div is an arbitrary value. It can be any html tag to output the same result 
      .data(dummyData)
      .enter()
    .append('circle')
      .attr('transform', d => `translate(${d.age}, 20)`)
      .attr('cx',32)
      .attr('cy',53)
      .attr('r',15);

 }

function example_4() {

   svg.selectAll('g')
      .data(dummyData)
      .enter()
    .append('circle')
      .attr('transform', d => `translate(${d.age}, 20)`)
      .attr('cx',32)
      .attr('cy',53)
      .attr('r',15);

    svg.selectAll('g')
      .data(dummyData)
      .enter()
    .append('path')
      .attr('stroke','#000')
      .attr('d',`M5,5H500`)
}

function example_5() {

   svg.selectAll('g')
      .data(dummyData)
      .enter()
    .append('circle')
      .attr('transform', d => `translate(${d.age}, 20)`)
      .attr('cx',32)
      .attr('cy',53)
      .attr('r',15);

    svg.selectAll('g')
      .data(dummyData)
      .enter()
    .append('g')
    .append('path')
      .attr('stroke','#000')
      .attr('d',`M5,5H500`)
}


example_1(); // This one creates DOM as: 

<svg>
  <g>
    <circle></circle>
    <circle></circle>
  </g>
</svg>

example_2(); // This one creates DOM as:

<svg>
  <g>
    <circle></circle>
    <circle></circle>
    <circle></circle>
    <circle></circle>
  </g>
</svg>

example_3(); // This one creates DOM as:

<svg>
  <g>
    <circle></circle>
    <circle></circle>
    <circle></circle>
    <circle></circle>
  </g>
</svg>

example_4(); // This one creates DOM as:

<svg>
  <g>
    <circle></circle>
    <circle></circle>
    <path></path>
    <path></path>
    <circle></circle>
    <circle></circle>
    <path></path>
    <path></path>
  </g>
</svg>

example_5(); // This one creates DOM as:

<svg>
  <g>
    <circle></circle>
    <circle></circle>
    <g>
      <path></path>
    </g>
    <g>
      <path></path>
    </g>
  </g>
</svg>

Переменная svg is domузел g как дочерний элемент SVG в моем примере.Это означает, что у него нет ни круга, ни g, ни div в качестве дочернего элемента.Тогда для чего используется метод selectAll?Почему мы не можем просто написать как

svg.data(dummyData)
  .enter()
.append('circle')
  .attr('transform', d => `translate(${d.age}, 20)`)
  .attr('cx',32).attr('cy',53)
  .attr('r',15);

Хотя я пробовал с более разными примерами, все ведет себя по-разному, но я действительно не вижу, что происходит за кулисами.Пожалуйста, помогите мне понять хотя бы немного.Я очень смущен.

1 Ответ

0 голосов
/ 23 июня 2018

Давайте сначала ответим на ваш последний вопрос.Почему вы не можете:

svg.data(dummyData).enter()

d3 использует шаблон ввода, обновления, выхода.На шаблоне есть тонны чтения, и я настоятельно рекомендую вам изучить его.Но это то, что нужно не только создавать начальные элементы, но и обновлять их позже..selectAll это найти все элементы, соответствующие моему селектору..data привязывает данные к ним..enter это скажите мне все элементы данных, которые не были в моем .selectAll.Таким образом, ответ на ваш вопрос: без .selectAll невозможно, чтобы d3 вычислил, какие данные вводят и ничего не добавляется.При классическом использовании при начальном рендеринге .selectAll возвращает пустой (ни один из них еще не существует), поэтому они все входят.

Теперь, с основами, давайте последуем вашему примеру 5:

svg.selectAll('g') //<-- no gs exist in svg
  .data(dummyData)
  .enter() //<-- so, since data is two elements
.append('circle') //<-- you get two circles
  .attr('transform', d => `translate(${d.age}, 20)`)
  .attr('cx',32)
  .attr('cy',53)
  .attr('r',15);

svg.selectAll('g') //<-- no gs still exist in svg
  .data(dummyData)
  .enter() //<-- so since data is two elements
.append('g') //<-- you get two gs
.append('path') //<-- each with a path under them
  .attr('stroke','#000')
  .attr('d',`M5,5H500`)

Теперь то, что вы делаете, на самом деле не имеет смысла здесь.Зачем выбирать g, если вы заботитесь о кругах?Обычно я бы даже не рекомендовал выбирать элементы вообще.Если я хочу, чтобы круг представлял каждый из моих данных , я бы сделал что-то вроде:

svg.selectAll('.my_cool_circle')
  .data(dummyData)
  .enter()
.append('circle')
.attr('class', 'my_cool_circle')
...

Используя класс, я знаю, что эти круги - это круги, представляющие мой dummyData.Кроме того, если мне нужно будет вернуться позже обновить мои круги (потому что мои данные сейчас:

dummyData = [
     {
       name: 'A',
       age: 50
     },
     {
       name: 'B',
       age: 20
     },
     {
       name: 'C',
       age: 30
     }          
   ]

).

Затем:

 svg.selectAll('.my_cool_circle')
  .data(dummyData)
  .enter()

Возвращает только данные "C", и я добавляю только 1 новый круг.

...