2014-01-30 3 views
1

Я новичок в D3, но люблю ее до сих пор. Но я знаю, что у моих решений нет ... элегантности.Нажмите таблицу, обновите строку, наведите указатель мыши на линию, обновите таблицу

Я пытаюсь иметь 2 элемента управления, таблицу и график, отображающий данные, представленные ячейками таблицы. Если вы нажмете на ячейку в таблице, соответствующая строка будет выделена. Если вы наведите указатель мыши на линию, связанная ячейка таблицы изменит цвет. В конце концов будет третий элемент управления, показывающий подробные данные, специфичные для этого cel. К сожалению, мне удалось выполнить эту работу, если я использую статические вызовы для функции обновления. Если я пытаюсь быть умным и динамичным, все это ломается.

Я попытался свести к минимуму мой пример настолько, насколько могу ниже. Линия таблицы click-> update работает, потому что вызовы SelectData(), которые обновляют все, используют постоянные данные. Однако наведение на линии не работает. В конце концов мне нужно, чтобы таблица была более динамичной, но пока, как я могу это исправить?

<!DOCTYPE html> 
<meta charset="utf-8"> 
<style> 
    .lineDefault { 
     fill: none; 
     stroke: red; 
     stroke-width: 1.5px; 
     stroke-dasharray: 4,4; 
    } 
    .axis path, 
    .axis line { 
     fill: none; 
     stroke: #000; 
     shape-rendering: crispEdges; 
    } 
</style> 
<body> 
    <div id="wrap"> 
     <table> 
      <tr> 
       <td id="dataBlock" onclick="SelectData(0)">1</td> 
       <td id="dataBlock" onclick="SelectData(1)">2</td> 
      </tr> 
      <tr> 
       <td id="dataBlock" onclick="SelectData(2)">3</td> 
       <td id="dataBlock" onclick="SelectData(3)">4</td> 
      </tr> 
     </table> 
     <div> 
      <svg class="chart"></svg> 
     </div> 
    </div> 
    <script src="http://d3js.org/d3.v3.min.js"></script> 
<script> 
    var width = 600, height = 600; 
    var maxx = 100, 
     maxy = 100; 

    var linedata = {}; 
    linedata[0] = [[0, 50 ],[ 50, 60 ],[100, 100]]; 
    linedata[1] = [[0, 40 ],[ 40, 40 ],[100, 90 ]]; 
    linedata[2] = [[0, 20 ],[ 50, 30 ],[100, 90 ]]; 
    linedata[3] = [[0, 0 ],[ 60, 30 ],[100, 30 ]]; 
    var activeElement = 0; 
    var graphlines = {}; 
    var numlines = 0; 

    chart = d3.select(".chart").attr("viewBox", "0 0 600 600").append("g"); 

    var x = d3.scale.linear().domain([0, maxx]).range([0, width]); 

    var y = d3.scale.linear().domain([0, maxy]).range([height, 0]); 

    var xAxis = d3.svg.axis().scale(x).orient("bottom"); 

    var yAxis = d3.svg.axis().scale(y).orient("left"); 

    var line = d3.svg.line() 
     .x(function(d) { return x(d[0]); }) 
     .y(function(d) { return y(d[1]); }); 

    for (var i = 0; i < 4; i++) { 
     graphlines[i] = chart 
      .append("path") 
      .datum(linedata[i]) 
      .attr("class", "lineDefault") 
      .attr("id", "linedata") 
      .attr("d", line) 
      .on("mouseover", SelectData(i)); 
     numlines++; 
    } 

    function SelectData(n) { 
     d3.selectAll("td").transition() 
      .style("background-color", function(d, i) { 
       return i == n ? "#c99" : "#fff"; 
      }); 
     activeElement = n; 
     for (var i = 0; i<numlines; i++) { 
      if (i == n) { 
       graphlines[i] 
        .style("stroke-dasharray", "1,0") 
        .transition() 
        .style("stroke-width", "3") 
        .style("stroke", "steelblue"); 
      } else { 
       graphlines[i] 
        .style("stroke-dasharray", "4,4") 
        .transition() 
        .style("stroke-width", "1.5") 
        .style("stroke", "red"); 
      } 
     } 
    } 

</script> 

MouseClick о влиянии таблицы на линии, то Mouseover на линиях не влияет на таблицу. Кроме того, я соглашусь на любые издевательства над моей элегантностью и указателями на их фиксацию.

ответ

3

Во-первых, причина, почему ваш метод Mouseover не работает, потому что в этой строке:

 .on("mouseover", SelectData(i)); 

вы вызываете метод SelectData в момент вызова метода .on(). Итак, когда вы закончите инициализацию, выбран последний элемент, но функция прослушивания события mouseover отсутствует. Вы хотите передать функцию имя в метод .on(). Если вы сделаете эту функцию, выполните два параметра (обычно с именем d и i), тогда d3 автоматически передаст значение индекса в качестве второго параметра. Я написал довольно длинную дискуссию о передаче функций в качестве параметров для кого-то недавно, you may find it useful.

Кроме того, вы действительно не пользуетесь структурой выбора d3, которая может сделать в одном вызове все, что вы используете for -loops. Я предлагаю потратить время на read through some tutorials на то, как работают функции d3.

Теперь на ваш главный вопрос:

Обычным решения для выбора элемента, данные которого совпадает с данными о кликнутом элементе, чтобы дать всем элементам класса, основанный на уникальный идентификатор из данных. Затем вы можете легко выбрать все элементы, связанные с данным объектом данных. Если пользователь выбирает объект с d.name=Sue, и вы инициализировали все элементы с этим именем в качестве имени класса, то вы можете сделать d3.select("path.Sue"), а также d3.select("td.Sue"), чтобы найти правильные.

Данные вашего примера, похоже, не имеют уникального значения идентификатора данных, а всего лишь номер индекса. Поэтому вам даже не нужен уникальный класс, вы можете просто использовать nth-of-type(i+1) CSS selector. (Это i+1, потому что подсчет CSS начинается с 1, а число отсчетов начинается с 0.)

Однако я бы предложил использовать специальный класс CSS для применения всех ваших стилей подсветки. Таким образом, будет легко выбрать текущие выделенные значения, чтобы удалить этот класс, вместо того, чтобы проходить через все, чтобы проверить, совпадает ли он. Вы можете использовать переходы CSS для перехода стиля после изменения класса.

Вот скриптовая версия вашего кода, тщательно подобранная для правильного использования d3 и с помощью вышеуказанного метода выделения данных.

http://fiddle.jshell.net/g8z5h/

Я не реализовали живую версию таблицы, но я рекомендую вам прочитать tutorial on nested selections, чтобы выяснить, как это будет работать.

Я дал каждому блоку данных свою собственную строку, чтобы селектор nth-of-type() работал правильно (нумерация элементов CSS работает только в том случае, если они все являются братьями и сестрами, они не могут быть элементами данных таблицы, разделенными на несколько строк). Если вам нужен исходный макет для работы, вам придется указывать имена классов классов на основе их значения индекса и использовать их для выбора. Я также переместил привязку события клика в код, потому что JSFiddle обертывает весь свой код в событии загрузки окна, поэтому функция SelectData не была видна вне его.

+1

P.S. Если вам не нравится эффект скольжения, когда линии меняются от пунктирной к твердой, определите свойство «stroke-dasharray» для сплошных линий, имеющих такую ​​же общую длину, что и штриховая линия. Например. с тире штриховки, составляющей '4 4', сплошная линия с одинаковой длиной равна' 8 0'. – AmeliaBR

+0

И один последний комментарий: ** значения 'id' должны быть уникальными для всей веб-страницы! ** Вы не можете использовать' id' для группировки похожих элементов, для чего предназначены классы. Вот почему я изменил 'id' на' class' в нескольких частях вашего кода. – AmeliaBR

+0

Я большой поклонник вашей @AmeliaBR! Большое вам спасибо за то, что нашли время, чтобы написать такие четкие, подробные и исчерпывающие объяснения ... не говоря уже о дополнительной миле, которую вы идете в приготовлении некоторых действительно твердых примеров. Такие люди, как вы, Ларс и многие другие (я бы хотел, чтобы я мог их перечислить), что частые сообщества Dover stackoverflow являются абсолютной идеей большой информации и доброй воли. Еще раз спасибо! – FernOfTheAndes

1

Я ссылаюсь на это, так как я хотел достичь того же в угловом2, используя d3. Вот мой код ...

private drawLine() { 
    this.line = d3Shape.line() 
     .y((d: any) => this.y(d.rank)) 
     .defined(function(d : any) { return d.rank}) 
     .x((d: any) => this.x(d.year)); 

    var color = function(i) { 
     var colors = ["#35559C","#D9469C","#70D45B","#915ABB","#FF7C26","#50C5F6","#ECBE4B"]; 
     return colors[i % colors.length]; 
    }; 

    var i = 0; 
    var j=1; 
    for(let d of this.lineData){ 
     this.g.append('path') 
      .attr('d', this.line(d.values)) 
      .attr("class", "line") 
      .attr("stroke", color(i)) 
      .attr("transform", "translate(0,"+this.margin.top+")") 
      .attr("id","row-"+j) 
      .on("mouseover",function(){ 
       d3.select(this) 
        .attr("class","line1"); 
       d3.select("tr#"+this.id) 
        .style("background-color","gainsboro") 
       //console.log(d3.select("tr#"+this.id)) 
      }) 
      .on('mouseout', function(){ 
       d3.select(this) 
        .attr("class","line"); 
       d3.select("tr#"+this.id) 
        .style("background-color","") 
      }); 

      j++; 
      i++; 
      if(i > 9) { 
       i = 0; 
      } 
    } 
} 
private mouseover(event){ 
d3.select(event.currentTarget) 
    .style("background-color","gainsboro") 
d3.select("path#"+event.currentTarget.id) 
    .attr("class","line1"); 

} 

private mouseout(event){ 
d3.select(event.currentTarget) 
    .style("background-color","") 
d3.select("path#"+event.currentTarget.id) 
    .attr("class","line"); 
} 

При создании строки я назначил ему идентификатор и тот же идентификатор для каждой строки в таблице.

<tr *ngFor="let object of jsonFile; let i = index" id="{{'row-'+[i+1]}}" 
    (mouseover)="mouseover($event)" (mouseout)="mouseout($event)"> 
    <td *ngFor="let column of columnHeaders" [ngStyle]="{'width': 
      column.length}"> 
     <div *ngIf="isString(column.columnType) == true"> 
      {{object[column.id] | trim}} 
     </div> 
     <div *ngIf="isString(column.columnType) == false"> 
      {{object[column.id]}} 
     </div> 
    </td> 
    </tr> 

и вызвал функцию mouseover и mouseout в строке таблицы. Я хотел бы получить некоторые рекомендации, если я ошибаюсь где-то .. :)

Смежные вопросы