D3.js : Является ли это хорошей практикой для привязки данных к DOM?

#d3.js #idioms

#d3.js #идиомы

Вопрос:

Я пытаюсь понять основы D3. Я понимаю, что очень важно правильно выполнить привязку.

Итак, я создал простую веб-страницу с операциями обновления, ввода и выхода. Каждый раз, когда данные изменяются (изменяются, добавляются, удаляются) Я вызываю функцию, в которой данные привязываются к svg-объектам способами обновления, ввода и выхода.

Интересно, хороший ли мой подход?

 <html>
<head>

<style>
  .drawarea { border-style: solid; }
</style>

<script src="https://d3js.org/d3.v4.min.js"></script>

<script>
var data, drawArea;

function init(){
  data = [{"px" : 25, "py" : 25, "s" : 5}, {"px" : 25, "py" : 50, "s" : 5}, {"px" : 25, "py" : 75, "s" : 10} ];

  drawArea = d3.select("#drawareadiv").append("svg:svg")
    .attr("class", "drawarea")
    .attr("width", 500)
    .attr("height", 250);

  draw();
}

function draw(){
  var domRects = drawArea.selectAll("rect");

  // update
  domRects
    .data(data)
    .attr("x", function(d) { return d.px; })
    .attr("y", function(d) { return d.py; })
    .attr("width", function(d) { return d.s; })
    .attr("height", function(d) { return d.s; });

  // enter
  domRects
    .data(data)
    .enter()
    .append("svg:rect")
    .attr("x", function(d) { return d.px; })
    .attr("y", function(d) { return d.py; })
    .attr("width", function(d) { return d.s; })
    .attr("height", function(d) { return d.s; });

   // exit
   domRects
     .data(data)
     .exit()
     .remove();
}

function updateDataAndBinding(){
  for(i=0; i<data.length; i  )
    data[i].px  = 10;
  draw();
}

function enterDataAndBinding(){
  var px = 25;
  var py = 25 * (data.length 1);
  var s = Math.floor(data.length/2)*5 5;
  data.push({"px" : px, "py" : py, "s" : s});
  draw();
}

function exitDataAndBinding(){
  data.splice(data.length-1, 1);
  draw();
}

</script>

</head>

<body onload="init()">
<div id="drawareadiv"></div>
<button onclick="updateDataAndBinding()">updateDataAndBinding</button>
<button onclick="enterDataAndBinding()">enterDataAndBinding</button>
<button onclick="exitDataAndBinding()">exitDataAndBinding</button>
</body>

</html>
 

Ответ №1:

Я бы предложил две вещи:

  1. Не связывайте данные снова и снова. Сделайте это только один раз:
     var domRects = drawArea.selectAll("rect").data(data);
     
  2. Поскольку это D3 v4.x, используйте merge для совместной работы с выборками «ввод» и «обновление»:
     // enter   update
    domRects.enter()
        .append("svg:rect")
        .attr("x", function(d) { return d.px; })
        .attr("y", function(d) { return d.py; })
        .attr("width", function(d) { return d.s; })
        .attr("height", function(d) { return d.s; })
        .merge(domRects)
        .attr("x", function(d) { return d.px; })
        .attr("y", function(d) { return d.py; })
        .attr("width", function(d) { return d.s; })
        .attr("height", function(d) { return d.s; });
     

Вот демонстрация (нажмите «выполнить фрагмент кода»):

 <style>
  .drawarea { border-style: solid; }
</style>

<script src="https://d3js.org/d3.v4.min.js"></script>

<script>
var data, drawArea;

function init(){
  data = [{"px" : 25, "py" : 25, "s" : 5}, {"px" : 25, "py" : 50, "s" : 5}, {"px" : 25, "py" : 75, "s" : 10} ];

  drawArea = d3.select("#drawareadiv").append("svg:svg")
    .attr("class", "drawarea")
    .attr("width", 500)
    .attr("height", 200);

  draw();
}

function draw(){
  var domRects = drawArea.selectAll("rect").data(data);

  // enter
  domRects.enter()
    .append("svg:rect")
    .attr("x", function(d) { return d.px; })
    .attr("y", function(d) { return d.py; })
    .attr("width", function(d) { return d.s; })
    .attr("height", function(d) { return d.s; })
		.merge(domRects)
		.attr("x", function(d) { return d.px; })
    .attr("y", function(d) { return d.py; })
    .attr("width", function(d) { return d.s; })
    .attr("height", function(d) { return d.s; });

   // exit
   domRects.exit()
     .remove();
}

function updateDataAndBinding(){
  for(i=0; i<data.length; i  )
    data[i].px  = 10;
  draw();
}

function enterDataAndBinding(){
  var px = 25;
  var py = 25 * (data.length 1);
  var s = Math.floor(data.length/2)*5 5;
  data.push({"px" : px, "py" : py, "s" : s});
  draw();
}

function exitDataAndBinding(){
  data.splice(data.length-1, 1);
  draw();
}

</script>

</head>

<body onload="init()">
<div id="drawareadiv"></div>
<button onclick="updateDataAndBinding()">updateDataAndBinding</button>
<button onclick="enterDataAndBinding()">enterDataAndBinding</button>
<button onclick="exitDataAndBinding()">exitDataAndBinding</button>
</body> 

Помимо этого, ваш подход кажется хорошим (и стандартным).