D3. js .each () с функцией, которая передает данные - PullRequest
1 голос
/ 06 мая 2020

Я хотел бы поместить свою всплывающую подсказку в функцию, чтобы я мог повторно использовать ее для нескольких элементов. Когда я вызываю функцию всплывающей подсказки для элемента метки, всплывающая подсказка отображает только первое значение x для всех меток вместо цикла по массиву значений X. Как мне правильно получить доступ к данным в функции?

const tooltip = d3.select('body').append('div')
     .attr('id', 'rect-tooltip');

function mouseover(data-x){
  d3.select('g')
  area.selectAll("text")
       .on('mouseover', (d) => {
       rect-tooltip.transition()
         .duration(100)
         .style('opacity', .9)
       rect-tooltip.html(`${data-x}`) //Pass in X-values
         .style('left', `${d3.event.pageX + 10}px`)
         .style('top', `${d3.event.pageY - 18}px`);
     })
     .on('mouseout', (d) => {
       rect-tooltip.transition()
         .duration(400)
         .style('opacity', 0);
     })

}  

const label = d3.select('g') 
      area.selectAll("text")
       .data(data)
       .join('text')
       .attr("class", "label")
       .text( (d)=> {return d.name;})
       .attr("x", (d)=> {return d.x;})
       .attr("y", (d)=> {return d.y;})
       .each(function(d) {
       mouseover(d.x);}); // Only first data point is added to each label?

Ответы [ 2 ]

1 голос
/ 06 мая 2020

Без этого примера я могу неправильно прочитать вашу проблему.

Проблема

Основная проблема возникает из-за итерации текстовых элементов дважды:

area.each(function (d) { 
   // do something with each element/datum in the selection
})

d3.selectAll("text")     
  .on("mouseover", function(d) {
  // apply an event listner and corrsesponding function to each text element.
})

Проблема в том, что второе вы вкладываете в первое. Для каждого элемента в area вы выбираете все элементы text: если у вас есть 2 элемента, вы дважды выделяете весь текст. Вам нужно только один раз выбрать каждый элемент text.

В имеющемся у вас шаблоне для каждого элемента в area мы передаем данные этого элемента во вложенную функцию, которая принимает свойство этого элемента данных и с d3.selectAll("text").on("mouseover" ... применяет эту одиночную к ко всем text событиям наведения мыши. Поскольку вы делаете это для каждого элемента в area, мы в конечном итоге перезаписываем прослушиватели событий несколько раз.

Нет, где вы ссылаетесь на текущую датум в цепочке, следующей за d3.selectAll("text"), поэтому у нас есть только значение из текущей базы данных в текущей итерации .each().

Решение

Вам не нужно использовать .each() здесь, чтобы применить прослушиватель событий, .on() должно быть достаточно.

У нас есть наши функции наведения и наведения указателя мыши:

function mouseover(d) {
    tooltip
       .style("opacity", 0.9)
       .text(d.x)
       .style('left', `${d3.event.pageX + 10}px`)
       .style('top', `${d3.event.pageY - 18}px`);
}
function mouseout() {
   tooltip
     .style("opacity",0);
}

Затем мы можем вызвать его с помощью:

selection.on("mouseover",mouseover) 
   .on("mouseout",mouseout);

И мы можем повторно использовать это для множественного выбора или элементов. Указанные датум c для каждого элемента будут использоваться для определения текста всплывающей подсказки.

var tooltip = d3.select(".tooltip");
var svg = d3.select("svg");

var data = [{x: 10},{x:50},{x:90},{x:130},{x:170},{x:210},{x:250},{x:290},{x:330}]

var g = svg.selectAll(null)
   .data(data)
   .enter()
   .append("g")
   .attr("transform",function(d) { return "translate("+[d.x,0]+")"; })
   
var rect = g.append("rect")
  .attr("width", 35)
  .attr("height", 100)
  .attr("fill","steelblue")
  .on("mousemove",mouseover)
  .on("mouseout",mouseout)
  
var text = g.append("text")
  .attr("y", 120)
  .attr("x", 18)
  .style("text-anchor","middle")
  .text(function(d) { return d.x; })
  .on("mouseover",mouseover)
  .on("mouseout",mouseout)  
   
function mouseover(d) {
    tooltip
       .style("opacity", 0.9)
       .text(d.x)
       .style('left', `${d3.event.pageX + 10}px`)
       .style('top', `${d3.event.pageY - 18}px`);
}
function mouseout() {
   tooltip
     .style("opacity",0);
}
.tooltip {
  position: absolute;
  padding: 5px;
  background: yellow;
}

rect, text {
  cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="tooltip"></div>
<svg width="400" height="300"></svg>
0 голосов
/ 07 мая 2020

Основываясь на комментарии Эндрю, я изменил свой код на:

const tooltip = d3.select('body').append('div')
     .attr('id', 'tooltip');

 function mouseover(d){
  tooltip.transition()
        .duration(100)
        .style('opacity', .9)
  tooltip.html(d)
        .style('left', `${d3.event.pageX + 10}px`)
        .style('top', `${d3.event.pageY - 18}px`);
}

function mouseout(){
    tooltip.transition()
         .duration(400)
         .style('opacity', 0);
}  

const label = d3.select('g') 
      area.selectAll("text")
       .data(data)
       .join('text')
       .attr("class", "label")
       .text( (d)=> {return d.name;})
       .attr("x", (d)=> {return d.x;})
       .attr("y", (d)=> {return d.y;})
       .on("mouseover", function(d) { mouseover(d.x); })
       .on("mouseout", mouseout);
...