Reputation: 781
I am trying to include a specific text in a div element when mouse is over a td tag.
my table:
<script>
d3.json("{% url "fund:data_simple_table" fund %}", function(error, data) {
if (error) throw error;
var columns = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'YTD'];
var table = d3.select('#returns').append('table');
var thead = table.append('thead');
var tbody = table.append('tbody');
// append the header row
thead.append('tr')
.selectAll('th')
.data(columns).enter()
.append('th')
.text(function (column) { return column; });
// create a row for each object in the data
var rows = tbody.selectAll('tr')
.data(data['values'])
.enter()
.append('tr');
// create a cell in each row for each column
var cells = rows.selectAll('td')
.data(function (row) {
return columns.map(function (column) {
return {column: column, value: row[column]};
});
})
.enter()
.append('td')
.style("color", function(d){ return d.value <= 0 ? "red" : "green"})
});
</script>
I have two datasets which have the same format but one with values and the second with text: data['values']
and data['text']
. I want to display the corresponding text when the mouse over the related value. So I try different things around the same idea:
d3.selectAll('td') // I tried also tbody.selectAll('td') or rows.selectAll('td')
.data(data['highlights'])
.each(function (d, i) {
return columns.map(function (column) {
return {column: column, value: i[column]};
});
})
.enter()
.on('mouseover', function(d, i) {
// Select the element by class, use .text to set the content
d3.select(".highlight").text(d['highlights']);
})
There is something wrong here. I got the error:
(index):478 Uncaught TypeError: d3.selectAll(...).data(...).each(...).enter(...).on is not a function.
I am not sure if it is the right approach. If so, what is wrong?
Many thanks in advance for your help.
data['values']: result = {list} : [{'': '2017', 'Jan': '', 'Feb': '', 'Mar': '', 'Apr': '', 'May': -0.0048, 'Jun': 0.0087, 'Jul': 0.0087, 'Aug': 0.005, 'Sep': 0.0182, 'Oct': 0.0079, 'Nov': 0.0088, 'Dec': 0.0104, 'YTD': 0.0642}, {'': '2017', 'Jan': '', 'Feb': '', 'Mar': '', 0 = {dict} : {'': '2017', 'Jan': '', 'Feb': '', 'Mar': '', 'Apr': '', 'May': -0.0048, 'Jun': 0.0087, 'Jul': 0.0087, 'Aug': 0.005, 'Sep': 0.0182, 'Oct': 0.0079, 'Nov': 0.0088, 'Dec': 0.0104, 'YTD': 0.0642} 1 = {dict} : {'': '2017', 'Jan': '', 'Feb': '', 'Mar': '', 'Apr': '', 'May': 0.0102, 'Jun': -0.0327, 'Jul': 0.0353, 'Aug': 0.0376, 'Sep': -0.0566, 'Oct': 0.0673, 'Nov': -0.0037, 'Dec': 0.0045, 'YTD': 0.0573} 2 = {dict} : {'': '2017', 'Jan': '', 'Feb': '', 'Mar': '', 'Apr': '', 'May': -0.0078, 'Jun': 0.0181, 'Jul': 0.0267, 'Aug': -0.0007, 'Sep': 0.0064, 'Oct': 0.0154, 'Nov': 0.0422, 'Dec': -0.0008, 'YTD': 0.1026} 3 = {dict} : {'': '2018', 'Jan': 0.0342, 'Feb': -0.0148, 'Mar': -0.0068, 'Apr': -0.0055, 'May': 0.003, 'Jun': 0.0002, 'Jul': '', 'Aug': '', 'Sep': '', 'Oct': '', 'Nov': '', 'Dec': '', 'YTD': 0.0094} 4 = {dict} : {'': '2018', 'Jan': 0.0668, 'Feb': -0.106, 'Mar': -0.0038, 'Apr': -0.0212, 'May': 0.0007, 'Jun': 0.0079, 'Jul': '', 'Aug': '', 'Sep': '', 'Oct': '', 'Nov': '', 'Dec': '', 'YTD': -0.0623} 5 = {dict} : {'': '2018', 'Jan': 0.0679, 'Feb': -0.0066, 'Mar': -0.0427, 'Apr': -0.0181, 'May': 0.0166, 'Jun': 0.0065, 'Jul': '', 'Aug': '', 'Sep': '', 'Oct': '', 'Nov': '', 'Dec': '', 'YTD': 0.02}
I put the final code below with the lemming's solution:
d3.json("{% url "fund:data_complex_table" fund %}", function(error, data) {
if (error) throw error;
var columns = ['Years', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'YTD'];
var table = d3.select('#full_returns').append('table');
var thead = table.append('thead');
var tbody = table.append('tbody');
var nb_rows = d3.selectAll(data['table']).size()
console.log(nb_rows);
// append the header row
thead.append('tr')
.selectAll('th')
.data(columns).enter()
.append('th')
.text(function (column) { return column; });
processedData = data['values'].map((row, rowIndex) => {
return columns.map(column => {
return {
column: column,
value: row[column],
highlight: data['highlights'][rowIndex][column]
};
});
});
// create a row for each object in the data
var rows = tbody.selectAll('tr')
.data(processedData)
.enter()
.append('tr')
.each(function (d,i){
if (i % 3 === 1) {
d3.select(this).attr("class", "row_fd").attr("text-anchor",
"start")
}
else if (i % 3 === 2 ) {
d3.select(this).attr("class", "row_bchk")
d3.select(this).style("font-size", "0.6em")
}
else if (i % 3 === 0) {
d3.select(this).attr("class", "row_mkt")
d3.select(this).style("font-size", "0.6em")
}
});
// create a cell in each row for each column
var cells = rows.selectAll('tr td')
.data(function(row) { return row; })
.enter()
.append('td')
.style("color", function(d){ return d.value <= 0 ? "red" :
"green"})
.text(d => d.value)
.on('mouseover', function(d) {
// Show the highlight
d3.select(".highlight").style('visibility', 'visible');
d3.select('.highlight').text(d.highlight);
d3.select(this).style("background-color", "#c1c1c1");
})
.on("mouseout", function(){
// Hide the highlight
d3.select(".highlight").style('visibility', 'hidden');
d3.select(this).style("background-color", "#f2f2f2");
})
.html(function(d){ return typeof(d.value)==="number"?
(100*d.value).toFixed(2)+"%":d.value;})
.each(function (d, i) {
if (i === 0){
d3.select(this).style("font-weight", "bold")
}
else if (i=== 13)
{
d3.select(this).style("font-weight", "bold")
}
});
d3.selectAll('tr.row_fd').selectAll('td')
.style('border-bottom-color', "#f2f2f2")
.style('border-top-color', "#f2f2f2");
d3.selectAll('tr.row_mkt').selectAll('td')
.style('border-bottom-color', "#f2f2f2")
.each(function (d, i) {
if (i === 0){
d3.select(this).text("mkt")
}});
d3.selectAll('tr.row_bchk').selectAll('td')
.style('border-top-color', "#f2f2f2")
.each(function (d, i) {
if (i === 0){
d3.select(this).text("bchk")
}});
});
</script>
Upvotes: 0
Views: 146
Reputation: 1873
I think that you are using the each()
function with the intention that it will transform the bound data for each element. But it is just for executing arbitrary code for each element in the current selection. Whatever you return will have no effect on the bound data.
It also looks like you would be binding completely new data (from data['highlights']
) to the table cells (td
), overwriting the previously bound data from data['values']
, which isn't what you want.
on-mouseover you are trying to select an element with a highlight
class. I can't see this element anywhere in your code, so I'm not sure if you intend for every td
to contain an initially hidden span.highlight
element, or if there would just be one element with this class on the page.
Solution
I think that you should pre-compute the data so that the values and the highlight text are in the same object. Assuming that data['values']
and data['highlights']
are always of the same length, you could do this by:
processedData = data['values'].map((row, rowIndex) => {
return columns.map(column => {
return {
column: column,
value: row[column],
highlight: data['highlight'][rowIndex][column]
};
});
});
Then you could create your table rows and cells, and set up the mouseover event as follows (this is assuming that there is just a single element with the highlight
class):
// create a row for each object in the data
var rows = tbody.selectAll('tr')
.data(processedData)
.enter()
.append('tr');
// create a cell in each row for each column
var cells = rows.selectAll('tr td')
.data(function(row) { return row; })
.enter()
.append('td')
.style("color", function(d){ return d.value <= 0 ? "red" : "green"})
.text(d => d.value)
.on('mouseover', function(d) {
d3.select('.highlight').text(d.highlight);
});
Upvotes: 1