Обновление с дочерними элементами DOM и распространением данных

Я хочу обновить DOM, содержащий дочерние элементы, с распространением данных.

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

Также удивительно, что старые данные можно было извлечь из дочерних элементов, против чего я категорически против, потому что данные слишком велики, и я не хочу, чтобы старые/лишние данные сохранялись, которые содержат ссылку и сохраняют старые данные. (Утечка памяти в моем понимании)

const DATA = [{
  name: "data1",
  left: 70,
  top: 70,
  radius: 20
}, {
  name: "data2",
  left: 200,
  top: 100,
  radius: 50
}];
const svg = d3.select("#svg");

// Not working as expected
function loadData(data) {
  console.info(data);
  const container = svg.selectAll(".container")
    .data(data);
  container.exit().remove();
  const enter = container.enter().append("g")
    .attr("class", "container");
  enter.append("circle");
  enter.append("text");

  container.merge(enter)
    .attr("transform", entry => `translate(0,${entry.top})`)
  svg.selectAll(".container circle")
    .attr("r", entry => entry.radius)
    .attr("cx", entry => entry.left);
  svg.selectAll(".container text")
    .text(entry => entry.name);
}

// Working, but not desired
function expected(data) {
  console.info(data);
  const container = svg.selectAll(".container")
    .data(data);
  container.exit().remove();
  const enter = container.enter().append("g")
    .attr("class", "container");
  enter.append("circle");
  enter.append("text");
  
  container.merge(enter)
    .attr("transform", entry => `translate(0,${entry.top})`)
  svg.selectAll(".container circle")
    .data(data)
    .attr("r", entry => entry.radius)
    .attr("cx", entry => entry.left);
  svg.selectAll(".container text")
    .data(data)
    .text(entry => entry.name);
}
let i = 0;
loadData(DATA);

function updateData() {
  i = (i+1) % 2;
  loadData(DATA.slice(i,i+1));
}
function expectedUpdate() {
  i = (i+1) % 2;
  expected(DATA.slice(i,i+1));
}
button {
  display: inline-block;
}
svg {
  width: 300px;
  height: 300px;
  display: block;
}
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button onclick = "updateData();">Update</button>
<button onclick = "expectedUpdate();">Expected</button>
<svg id = "svg"></svg>

Это просто демонстрация, где у меня есть родительский элемент <g>, и к нему добавляются 2 дочерних элемента <circle> и <text>.

В первый раз, когда он отображается, я использовал метод loadData() для заполнения DOM, соответствующие дочерние DOM (круг и текст) смогли получить данные, вставленные в их родительский DOM (g), что показало правильное распространение данных по DOM. дерево.

Однако, когда я нажимаю кнопку «обновить» несколько раз, обновляется только родительский DOM, а дочерние DOM могут получить доступ к несуществующим данным (старым данным).

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

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
3
0
68
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Основная проблема в вашей функции нерабочий (loadData) заключается в том, что вы используете selectAll. В отличие от select, selectAllне распространяет данные.

Взгляните на эту таблицу, которую я составил, резюмируя различия между ними:

МетодВыбрать()выбрать все()
Выборвыбирает первый элемент, который соответствует строке селекторавыбирает все элементы, соответствующие строке селектора
ГруппировкаНе влияет на группировкуВлияет на группировку
Распространение данныхРаспространяет данныеНе распространяет данные

Как видите, основная информация для вас такова:

  • select: распространяется данные.
  • selectAll: не распространяется данные.

Учитывая, что в вашем фрагменте всего один дочерних элементов (круг и текст) на группу, самое простое решение — просто изменить selectAll на select. Однако, если у вас есть более одного дочернего элемента для каждого родителя, лучшим (и идиоматическим) решением является создание вложенного выбора ввода-обновления-выхода, что немного более трудоемко.

Кроме того, вам не хватает ключевой функции:

const container = svg.selectAll(".container")
    .data(data, function(d) {
        return d.name;
    });

Вот ваш код с этими изменениями:

const DATA = [{
  name: "data1",
  left: 70,
  top: 70,
  radius: 20
}, {
  name: "data2",
  left: 200,
  top: 100,
  radius: 50
}];
const svg = d3.select("#svg");

// Not working as expected
function loadData(data) {
  console.info(data);
  const container = svg.selectAll(".container")
    .data(data, function(d) {
      return d.name;
    });
  container.exit().remove();
  const enter = container.enter().append("g")
    .attr("class", "container");
  enter.append("circle");
  enter.append("text");

  container.merge(enter)
    .attr("transform", entry => `translate(0,${entry.top})`)
  svg.select(".container circle")
    .attr("r", entry => entry.radius)
    .attr("cx", entry => entry.left);
  svg.select(".container text")
    .text(entry => entry.name);
}

// Working, but not desired
function expected(data) {
  console.info(data);
  const container = svg.selectAll(".container")
    .data(data);
  container.exit().remove();
  const enter = container.enter().append("g")
    .attr("class", "container");
  enter.append("circle");
  enter.append("text");

  container.merge(enter)
    .attr("transform", entry => `translate(0,${entry.top})`)
  svg.selectAll(".container circle")
    .data(data)
    .attr("r", entry => entry.radius)
    .attr("cx", entry => entry.left);
  svg.selectAll(".container text")
    .data(data)
    .text(entry => entry.name);
}
let i = 0;
loadData(DATA);

function updateData() {
  i = (i + 1) % 2;
  loadData(DATA.slice(i, i + 1));
}

function expectedUpdate() {
  i = (i + 1) % 2;
  expected(DATA.slice(i, i + 1));
}
button {
  display: inline-block;
}

svg {
  width: 300px;
  height: 300px;
  display: block;
}
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button onclick = "updateData();">Update</button>
<button onclick = "expectedUpdate();">Expected</button>
<svg id = "svg"></svg>

Привет, Херардо, спасибо за ответ на вопрос! Очень ценю! Мне каким-то образом удалось решить мою настоящую модель, но я совершенно не понимаю, что происходит. Я вижу, что ключевая функция является важным фактором в этом примере, однако, когда я хочу добавить анимацию перехода, я вижу, что весь график перерисовывается, а не обновляется. Я хотел бы знать, как работает ключевая функция, если вы не возражаете, спасибо!

ShiraishiMai 03.06.2019 11:56

@ShiraishiMai Это хорошее объяснение функции клавиши: bost.ocks.org/mike/constancy

Gerardo Furtado 03.06.2019 12:05

Другие вопросы по теме