2016-07-31 65 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