2016-07-31 67 views
0

我有以下HTML结构来表示一个日历:D3/CSS大教堂向上穿越/选择

<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可以实现吗?

+1

将使用'div'的好起来的,而不是一个表? – LGSon

+0

结构从库(Pikaday.js)返回,所以我不能改变它 – Sean

+1

只是一个方面的评论,与你的问题没有关系:当你说“遍历”时,人们关于去DOM中的父节点树。你的情况并不完全是“遍历”,因为td'is-startrange'是一个tr,它是一个兄弟(关于tr的按钮)。 –

回答

2

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

有了这个选择在手就可以申请一类,说range,通过使用selection.classed(names[, value])这可能需要传递的第二个参数value功能:

如果是一个函数,然后该功能为每个选定的元件进行评价,为了,正在传递的当前原点(d),当前指数(),和当前组(节点),与此作为当前的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>

+0

美丽的答案,upvoted。 –

+0

非常感谢你的回答!然而,我有一个问题:如果我点击“12”,rangeFilter(12)将针对每个td元素进行评估,并且'var inRange = false'将在每个选定元素的开始处运行?然后检查似乎不起作用。在我看来,rangeFilter(12)仅在第一个元素上运行,并且它返回的函数用于其余元素?对不起,我对JavaScript很新颖。 – Sean

+0

@Sean你说得很对。该方法使用了两个在JS土地中相当常见的特征,但对于初学者很难理解。 (1)[Callbacks](https://developer.mozilla.org/en-US/docs/Mozilla/js-ctypes/Using_js-ctypes/Declaring_and_Using_Callbacks),例如,在'classed()'中将被*调用回每个元素。函数'rangeFilter'只会在每次点击事件中被调用一次,并且会返回为该迭代的每个元素调用的回调函数。这使我们(2)[闭包](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)。这个概念用于 – altocumulus