D3/CSS Dom向上遍历/选择



>我有以下HTML结构来表示日历:

<table>
  <thead>...</thead>
  <tbody>
    <tr>...</tr>
    <tr>
      <td day="4">...</td>
      <td day="5">...</td>
      <td day="6" class="is-startrange">...</td>
      <td day="7">...</td>
      <td day="8">...</td>
    </tr>
    <tr>
      <td day="9">...</td>
      <td day="10">...</td>
      <td day="11">...</td>
      <td day="12">
        <button class="day" type="button">12</button>
      </td>
      <td day="13">...</td>
    </tr>
  </tbody>
</table>

我的问题是:从第 12 天的按钮开始,如何向上遍历,选择所有按钮元素,直到遇到is-startrange类?每个表单元格都是一个表示日期的按钮,侦听器已添加到所有按钮元素中。单击日期时,我将获得所选日期作为起点。

我想为开始日期和选定日期之间的所有按钮元素添加样式(添加类或通过纯 CSS(。

有没有办法在 D3 选择或纯 CSS 中实现这一点?

正如Gerardo Furtado在他的评论中已经提到的,这个问题实际上不是关于向上遍历DOM,而是关于td元素的迭代。这可以通过使用d3.selectAll("td")轻松完成,这将产生页面上找到的所有td的扁平选择。根据您的布局,您可能需要进一步将选择范围缩小到特定的表格,这可以通过将选择器调整为"table.myTable td""#tableId td"等来完成。

有了这个选择,你可以应用一个类,比如range,通过使用selection.classed(names[, value]),它可以接受传入的函数作为第二个参数value

如果是一个函数,则为每个选定的元素计算该函数,按顺序传递当前基准 (d(、当前索引 (i( 和当前组(节点(,并将其作为当前 DOM 元素。然后,函数的返回值用于在每个元素上分配或取消分配类。

剩下的唯一任务是实现一个过滤器函数,该函数跟踪元素是否在所需或范围内,从而确定是否分配range类。

以下代码片段显示了如何使用提供给.classed()的过滤器函数rangeFilter()将所有这些组合在一起:

// The day parameter determines the stop criterion
function rangeFilter(day) {  
  // This property is closed over by the following function to keep track of the
  // range. If this is true, this element and following elements belong to the
  // range until this property becomes false again once reaching the button's td.
  var inRange = false;   
  
  // Filter function returning true, if the element belongs to the range.
  return function(d) {
    element = d3.select(this);   // The actual td element of this iteration step.
    // Evaluate if the element is still in the range or, in case the range has not
    // yet started, check if we reached the td.is-startrange.
    inRange = (inRange && element.attr("day") != day) 
            || element.classed("is-startrange");      
    // XOR to exclude the .is-startrange element.
    return inRange != element.classed("is-startrange"); 
  }
}
d3.selectAll("button")
  .on("click", function() {
    // For all tds check if they belong to the range and set the class based
    //  on the result of the filter function passing in this buttons value.
    d3.selectAll("td")
        .classed("range", rangeFilter(d3.select(this).text())); 
  });
.is-startrange {
  background-color: limegreen;
}
.range {
  background-color: red;
}
<script src="https://d3js.org/d3.v4.js"></script>
<h1>Hit the button</h1>
<table>
  <thead>...</thead>
  <tbody>
    <tr>...</tr>
    <tr>
      <td day="4">...4...</td>
      <td day="5">...5...</td>
      <td day="6" class="is-startrange">...6...</td>
      <td day="7">...7...</td>
      <td day="8">...8...</td>
    </tr>
    <tr>
      <td day="9">...9...</td>
      <td day="10">...10...</td>
      <td day="11">...11...</td>
      <td day="12">
        <button class="day" type="button">12</button>
      </td>
      <td day="13">...13...</td>
    </tr>
  </tbody>
</table>

最新更新