如何使用d3.js创建<dl>

时间:2018-05-01 19:41:58

标签: javascript d3.js

我想使用d3.js从一些数据列表中创建一系列dl标记。

我提出的代码是:

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>

我已经做了相当多的谷歌搜索,没有回答这个问题,至少是not in a way that works in v5not with more than one dt/dd pair

这似乎是d3.js应该能做的基本事情。

3 个答案:

答案 0 :(得分:5)

在您的解决方案中:

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

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

附加了enter().append()周期的所有元素将按照它们的附加顺序附加到父级,对于您运行的顺序如下:首先是所有dt s,然后是{{1}正如你所看到的那样。由enter语句创建的占位符节点(这些不是附加元素)不会以您希望它们出现的方式嵌套子节点。

尽管d3不包含使用简单dd方法轻松实现所需方法的方法,但使用标准d3方法和额外步骤可以非常轻松地实现所需的行为或两个。或者,我们可以自己在d3.selection中构建该功能。

对于我的回答,我将完成一个使用您的数据结构并输入模式的示例,但是为了开始我将在这里简化嵌套 - 而不是嵌套的附加我只是演示了几种可能的附加方法有序的兄弟姐妹。首先,我还简化了数据结构,但原理保持不变。

第一种方法可能是最直接的:使用selection.append()函数。使用输入选择(使用父级或输入的占位符),使用each方法追加两个单独的元素:

selection.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; })
    
})

但是,也许更优雅的选择是挖掘d3.selection()并用它玩具给我们一些新的行为。下面我添加了一个<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>方法,可以让您在选择中的每个项目的正下方追加一个配对的兄弟元素:

selection.appendSibling()

它选择每个节点,在指定类型中创建新的配对兄弟节点(每个节点紧接在DOM中的原始节点之后),然后将新节点放在d3选择中并绑定数据。这允许您将方法链接到它上以设置元素的样式等,并允许您访问绑定的基准。请参阅下面的操作:

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());
}
// 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

当然,将兄弟姐妹分开选择是很明智的,这样你就可以管理每个兄弟姐妹的更新/进入/退出等。

此方法非常容易应用于您的示例,这是一个嵌套解决方案,使用的结构与您期望的结果和appendSibling方法一样:

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
// 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]; })

答案 1 :(得分:1)

使用.html appender(而不是.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的精神,并且难以使用附加的子项(添加类,样式,其他子项,......)。

答案 2 :(得分:0)

<table class="table table-bordered table-responsive table-hover"> <tr> <th>No.</th> <th>Number Licens</th> <th>Full Name</th> <th>Phone Number</th> <th>Start Work</th> <th>Date Cheking the Eyes</th> <th>Address</th> <th>Email</th> <th>Edit</th> <th>Delete</th> </tr> @foreach (Drivers p in ViewBag.Drivers) { <tr> <td>@p.Line</td> <td>@p.NumberLicens</td> <td>@p.FirstName &nbsp; @p.LastName </td> <td>@p.PhoneNumber</td> <td>@p.StartWork</td> <td>@p.DateCheckEyes</td> <td> @Html.ActionLink("Edit", "MyAction", "DriverTaxi", new { id = p.Line }, null) </td> <td>addres</td> <td>email</td> <td><input id="Button2" type="submit" value="Delete" name="@p.NumberLicens" /></td> </tr> }`enter code here` 的示例很有用。但是,您是否尝试直接附加到data本身,而不是追加到x

dt