Как создать <dl> с помощью d3.js

Я хотел бы создать серию тегов dl в списке из некоторых данных с помощью d3.js.

Код, который я придумал, таков:

var x=d3.select("body")
    .append('ol')
    .selectAll('li')
    .data(data)
    .enter()
    .append('li')
    .append('dl')
    .selectAll()
    .data(d=>Object.entries(d.volumeInfo)).enter();

x.append('dt')
    .text(d=>d[0]);

x.append('dd')
    .text(d=>d[1]);

где data - массив объектов. Все работает, кроме элементов в неправильном порядке.

Вот порядок, который мне удалось получить:

<dl>
    <dt>key1</dt>
    <dt>key2</dt>
    <dd>value1</dd>
    <dd>value2</dd>
</dl>

Но должно быть так:

<dl>
    <dt>key1</dt>
    <dd>value1</dd>
    <dt>key2</dt>
    <dd>value2</dd>
</dl>

Я много погуглил, и ничего не ответил на вопрос, по крайней мере, не так, как в v5 или не более чем с одной парой dt / dd.

Похоже, это что-то базовое, что должен уметь d3.js.

что в data? кроме того, не приведет ли append('dl').selectAll().data(...) в странную ситуацию? Можете ли вы сделать let y = ... , который является вашим <dl>, а затем сначала добавить к нему элементы dt / dd, а затем, только когда это будет сделано, добавить его в x?

Mike 'Pomax' Kamermans 01.05.2018 22:58
Поведение ключевого слова "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) для оценки ваших знаний,...
6
1
161
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Пример для data был бы полезен. Но вместо добавления в x пробовали ли вы добавлять непосредственно в dt?

x.append('dt').text(d=>d[0]).append('dd')

Вот возможность использования приложения .html (вместо .append):

var data = [
  { "volumeInfo": { "key1": "value1", "key2": "value2" }, "some": "thing" },
  { "volumeInfo": { "key3": "value3", "key4": "value4", "key5": "value5" } }
];

d3.select("body")
  .append('ol')
  .selectAll('li')
  .data(data)
  .enter()
  .append('li')
  .append('dl')
  .html( function(d) {
    // Produces: <dt>key1</dt><dd>value1</dd><dt>key2</dt><dd>value2</dd>
    return Object.entries(d.volumeInfo).map(r => "<dt>" + r[0] + "</dt><dd>" + r[1] + "</dd>").join("");
  });
dt { float: left; width: 100px; }
dd { margin-left: 100px; }
<script src = "https://d3js.org/d3.v5.min.js"></script>

который производит это дерево:

<ol>
  <li>
    <dl>
      <dt>key1</dt>
      <dd>value1</dd>
      <dt>key2</dt>
      <dd>value2</dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt>key3</dt>
      ...
    </dl>
  </li>
</ol>

Обратите внимание, что это не совсем в духе d3 и затрудняет работу с добавленными дочерними элементами (добавление класса, стиля, других дочерних элементов, ...).

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

В вашем решении:

x.append('dt')
    .text(d=>d[0]);

x.append('dd')
    .text(d=>d[1]);

Все элементы, добавленные с помощью цикла enter().append(), добавляются к родительскому элементу в том порядке, в котором они добавляются, что для вас работает следующим образом: сначала все dt, затем все dd, как вы видели. Узлы-заполнители (это не добавленные элементы), созданные оператором ввода, не вкладывают дочерние элементы так, как вы можете от них ожидать.

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

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


Первый способ может быть самым простым: использовать функцию selection.each(). При выборе ввода (либо с родительским элементом, либо с введенными заполнителями) используйте метод each, чтобы добавить два отдельных элемента:

var data = [
{name:"a",description:"The first letter"},
{name:"b",description:"The second letter"}
];


d3.select("body")
  .selectAll(null)
  .data(data)
  .enter()
  .each(function(d) {
	var selection = d3.select(this);
	
	// append siblings:
	selection.append("dt")
	  .html(function(d) { return d.name; });
	selection.append("dd")
	  .html(function(d) { return d.description; })
    
})
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

Но, возможно, более элегантный вариант - покопаться в d3.selection () и поиграть с ним, чтобы дать нам какое-то новое поведение. Ниже я добавил метод selection.appendSibling(), который позволяет вам добавлять парный элемент-брат сразу под каждым элементом в выделенном фрагменте:

d3.selection.prototype.appendSibling = function(type) {
    var siblings =  this.nodes().map(function(n) {
        return n.parentNode.insertBefore(document.createElement(type), n.nextSibling);
    })
    return d3.selectAll(siblings).data(this.data());
}

Он берет каждый узел в выбранном элементе, создает новый парный одноуровневый узел (каждый сразу после исходного узла в DOM) указанного типа, а затем помещает новые узлы в выборку d3 и связывает данные. Это позволяет вам связывать методы с ним для стилизации элемента и т. д. И дает вам доступ к привязанной базе данных. Посмотрите это в действии ниже:

// modify d3.selection so that we can append a sibling
d3.selection.prototype.appendSibling = function(type) {
  var siblings =  this.nodes().map(function(n) {
    return n.parentNode.insertBefore(document.createElement(type), n.nextSibling);
  })
  return d3.selectAll(siblings).data(this.data());
}

var data = [
    {name:"a",description:"The first letter"},
    {name:"b",description:"The second letter"}
    ];

d3.select("body")
  .selectAll(null)
  .data(data)
  .enter()
  .append("dt")
  .html(function(d) { return d.name; })
  .appendSibling("dd")  // append siblings
  .html(function(d) { return d.description; })  // modify the siblings
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

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

Этот метод очень легко применить к вашему примеру, вот вложенное решение, использующее данные, которые структурированы так, как вы ожидаете, и метод appendSibling:

// modify d3.selection so that we can append a sibling
d3.selection.prototype.appendSibling = function(type) {
  var siblings =  this.nodes().map(function(n) {
    return n.parentNode.insertBefore(document.createElement(type), n.nextSibling);
  })
  return d3.selectAll(siblings).data(this.data());
}

var data = [
 {volumeInfo: {"a":1,"b":2,"c":3}},
 {volumeInfo: {"α":1,"β":2}}
]

var items = d3.select("body")
    .append('ol')
    .selectAll('li')
    .data(data)
    .enter()
    .append('li')
    .append('dl')
    .selectAll()
    .data(d=>Object.entries(d.volumeInfo)).enter();
  
var dt = items.append("dt")
  .text(function(d) { return d[0]; })
  
var dd = dt.appendSibling("dd")
  .text(function(d) { return d[1]; })
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

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