2016-07-31 17 views
0

Ich habe die folgende HTML-Struktur einen Kalender darzustellen:D3/CSS Dom nach oben Traversal/Auswahl

<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> 

Meine Frage ist: vom Button unter Tag ab dem 12., wie kann ich nach oben durchqueren, wählen Sie alle Schaltflächenelemente, bis eine Klasse angetroffen wird? Jede Tabellenzelle ist eine Schaltfläche, die ein Datum darstellt, und Listenelemente wurden allen Schaltflächenelementen hinzugefügt. Wenn auf ein Datum geklickt wird, erhalte ich das ausgewählte Datum als Startpunkt.

Ich möchte alle Schaltflächenelemente zwischen dem Startdatum und dem ausgewählten Datum Stil hinzufügen (entweder Klasse hinzufügen oder durch reines CSS).

Gibt es eine Möglichkeit dies in D3-Auswahl oder reinem CSS zu erreichen?

+1

Would mit 'div' der anstelle einer Tabelle in Ordnung sein? – LGSon

+0

die Struktur wird von einer Bibliothek (Pikaday.js) zurückgegeben, so kann ich es nicht ändern – Sean

+1

Nur ein Seitenkommentar, nicht zu Ihrer Frage verwandt: Wenn Sie sagen "Traversing Up", Menschen Dinge über die Elternknoten im DOM gehen Baum. Dein Fall ist nicht gerade "nach oben", weil der td "ist-Startrange" in einem tr ist, der ein Geschwister ist (bezüglich des tr des Knopfes). –

Antwort

2

Wie schon Gerardo Furtado in seiner comment erwähnt, geht es eigentlich nicht darum, das DOM nach oben zu durchwandern, sondern vielmehr um eine Iteration von td Elementen. Dies kann einfach unter Verwendung von d3.selectAll("td") durchgeführt werden, was zu einer flacheren Auswahl aller auf der Seite gefundenen td s führt. Abhängig von Ihrem Layout müssen Sie die Auswahl möglicherweise weiter auf eine bestimmte Tabelle eingrenzen, die durch Anpassen des Selektors an "table.myTable td", "#tableId td" oder dergleichen vorgenommen werden kann.

diese Auswahl bei der Hand zu haben Sie eine Klasse anwenden können, sagen range, durch selection.classed(names[, value]) mit denen eine Funktion übergeben als zweites Argument value nehmen kann:

Wenn der Wert ist eine Funktion, dann die Funktion wird für jedes ausgewählte Element ausgewertet wird, um das aktuelle Datum (d) geleitet wird, den aktuellen Index (i ) und der aktuellen Gruppe (Knoten) mit diese als aktuelles DOM-Element. Der Rückgabewert der Funktion wird dann verwendet, um Klassen für jedes Element zuzuweisen oder aufzuheben.

Die einzige Aufgabe der linke Seite ist eine Filterfunktion, die Spur hält zu implementieren, wenn ein Element in dem gewünschten oder Bereich liegt oder nicht, und bestimmt somit, ob die range Klasse zuzuordnen.

Der folgende Ausschnitt zeigt, wie das alles zusammen eine Filterfunktion rangeFilter() zu .classed() versehen mit genommen werden konnte:

// 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

Schöne Antwort, upvoted. –

+0

Vielen Dank für die Antwort!Ich habe jedoch eine Frage: Wenn ich auf "12" klicke, wird rangeFilter (12) für jedes td-Element ausgewertet und 'var inRange = false 'wird am Anfang für jedes ausgewählte Element ausgeführt. Dann scheint die Überprüfung nicht zu funktionieren. Es scheint mir, dass RangeFilter (12) nur bei den ersten Elementen ausgeführt wird und die zurückgegebene Funktion für den Rest der Elemente verwendet wird? Entschuldigung, ich bin ziemlich neu in Javascript. – Sean

+0

@Sean Du hast recht. Der Ansatz verwendet zwei Merkmale, die in JS Land ziemlich üblich sind, aber für Anfänger schwer zu verstehen. (1) [Rückrufe] (https://developer.mozilla.org/en-US/docs/Mozilla/js-ctypes/Using_js-ctypes/Declaring_and_Using_Callbacks), die z.B. in 'classed()' wird * für jedes Element * zurückgerufen. Die Funktion 'rangeFilter' wird nur einmal pro Klick-Ereignis aufgerufen und gibt die Callback-Funktion zurück, die für jedes Element dieser Iteration aufgerufen wird. Dies bringt uns zu (2) [Closures] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures). Dieses Konzept wird verwendet, um – altocumulus