responsive dc.js chart is taking over the whole window - dc.js

I'm having trouble making my chart responsive, I'm trying to apply the same example with DC.js and Crossfilter that is at this link: resizing-series.
The chart is responsive, however, there are some bugs when interacting with the tables I have. For example, when I click on either the graph or any of the tables, the graph becomes very large, occupying the entire screen. As shown in the image below:
When I leave the screen split with Console, the graph is rendered responsively, as I change the screen size the size of the graph is also changed, however, if I leaveConsole the graph remains occupying the entire the screen and does not return to the default initial size shown in the first image.
Does anyone know how to tell me how to fix these bugs? I couldn't understand why this is happening.
Thnaks in advance.
var composite = dc.compositeChart('#composite');
var vendedorTable = dc.dataTable("#vendedores");
var citiesTable = dc.dataTable("#cities");
function remove_empty_bins(source_group) {
return {
top: function(N) {
return source_group.all().filter(function(d) {
return d.value.totalAno > 1e-3 ||
d.value.totalHomologo > 1e-3;
}).slice(0, N);
}
};
}
var url = 'https://gist.githubusercontent.com/bernalvinicius/3cece295bc37de1697e7f83418e7fcc9/raw/a5820379ec6eae76ee792495cc5dd1685c977a73/vendedores.json';
d3.json(url).then(function(data) {
data.forEach(d =>
Object.assign(d, {
mes: d.Month,
atual: d.Vendas_Ano,
passado: d.Vendas_Ant
})
);
var cf = crossfilter(data);
vendedorDim = cf.dimension(function(d) {
return d.vendnm;
});
var vendedorGroup = vendedorDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
citiesDim = cf.dimension(function(d) {
return d.zona;
});
var citiesGroup = citiesDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
var dim = cf.dimension(dc.pluck('mes')),
grp1 = dim.group().reduceSum(dc.pluck('atual')),
grp2 = dim.group().reduceSum(dc.pluck('passado'));
var minMonth = dim.bottom(1)[0].mes;
var maxMonth = dim.top(1)[0].mes;
var all = cf.groupAll();
dc.dataCount(".dc-data-count")
.dimension(cf)
.group(all);
function reduceAdd(p, v) {
p.totalAno += +v.Vendas_Ano;
p.totalHomologo += +v.Vendas_Ant;
return p;
}
function reduceRemove(p, v) {
p.totalAno -= v.Vendas_Ano;
p.totalHomologo -= v.Vendas_Ant;
return p;
}
function reduceInitial() {
return {
totalAno: 0,
totalHomologo: 0,
};
}
// Fake Dimension
rank = function(p) {
return ""
};
// Chart by months
composite
.width(600)
.height(300)
.x(d3.scaleLinear().domain([1, 12]))
.yAxisLabel("")
.xAxisLabel("Month")
.legend(dc.legend().x(500).y(0).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.compose([
dc.lineChart(composite)
.dimension(dim)
.colors('steelblue')
.group(grp1, "Currently Year"),
dc.lineChart(composite)
.dimension(dim)
.colors('darkorange')
.group(grp2, "Last Year")
])
.brushOn(true);
composite.brush().extent([-0.5, data.length + 1.5])
composite.extendBrush = function(brushSelection) {
if (brushSelection) {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
const point = Math.round((brushSelection[0] + brushSelection[1]) / 2);
return [
point - 0.5,
point + 0.5
];
}
};
// Sales Table
vendedorTable.width(500)
.height(480)
.dimension(remove_empty_bins(vendedorGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
// Cities Table
citiesTable.width(500)
.height(480)
.dimension(remove_empty_bins(citiesGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
// Sales click events
vendedorTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
vendedorDim.filter(null);
else
vendedorDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
// Cities click events
citiesTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
citiesDim.filter(null);
else
citiesDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
vendedorTable.filter(null);
vendedorDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
dc.renderAll();
// reset functions
$('#reset').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
$('#resetTable').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
$('#resetTable2').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
/****************************************************************************/
// Functions to handle responsive
var adjustX = 10,
adjustY = 40;
apply_resizing(composite, adjustX, adjustY, function(composite) {
composite.legend().x(window.innerWidth - 200);
});
var find_query = function() {
var _map = window.location.search.substr(1).split('&').map(function(a) {
return a.split('=');
}).reduce(function(p, v) {
if (v.length > 1)
p[v[0]] = decodeURIComponent(v[1].replace(/\+/g, " "));
else
p[v[0]] = true;
return p;
}, {});
return function(field) {
return _map[field] || null;
};
}();
var resizeMode = find_query('resize') || 'widhei';
function apply_resizing(composite, adjustX, adjustY, onresize) {
if (resizeMode === 'viewbox') {
composite
.width(300)
.height(200)
.useViewBoxResizing(true);
d3.select(composite.anchor()).classed('fullsize', false);
} else {
adjustX = adjustX || 0;
adjustY = adjustY || adjustX || 0;
composite
.width(window.innerWidth - adjustX)
.height(window.innerHeight - adjustY);
window.onresize = function() {
if (onresize) {
onresize(composite);
}
composite
.width(window.innerWidth - adjustX)
.height(window.innerHeight - adjustY);
if (composite.rescale) {
composite.rescale();
}
composite.redraw();
};
}
}
});
#composite {
padding: 10px;
}
.dc-table-group {
visibility: collapse;
}
tr.dc-table-row.sel-rows {
background-color: lightblue;
}
.brush .custom-brush-handle {
display: none;
}
<!-- favicon -->
<link rel="shortcut icon" href="https://img.icons8.com/nolan/64/puzzle.png">
<!-- bootstrap.css -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- bootstrap-theme.css -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- dc.css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.8/dc.css">
<!-- jquery.js -->
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<!-- bootstrap.js -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- d3.v5.js -->
<script src="https://d3js.org/d3.v5.js"></script>
<!-- crossfilter.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<!-- dc.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.1.8/dc.js"></script>
<title>12</title>
</head>
<body>
<div class="container-fluid">
<div class="row content">
<div class="col-md-10">
<div class="col-md-4">
<div id="composite"></div>
</div>
</div>
</div>
<div class="row content">
<div class="col-md-10">
<div style="padding: 20px;;" class="row marginClass">
<h4 class="pull-left" id="Introduction">
<small>Fictitious company data | Drilldown Example |</small>
</h4>
<h6 class="dc-data-count" style="float: left;margin-left:5px;">
<span>
<span class="filter-count"></span> selected from
<span class="total-count"></span> records |
<a id="reset"> Reset </a>
</span>
</h6>
</div>
<div class="col-md-4">
<table class="table" id="vendedores">
<thead>
<tr>
<th>Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-4">
<table class="table" id="cities">
<thead>
<tr>
<th>City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</body>

Thanks for posting an example! The bug doesn't show up in the SO code snippet feature unless you go to full page mode. I found your fiddle easier to work with.
Note: this is a complete rewrite of my previous answer, which was not clear.
1. Using ResizeObserver
updated 4/28/20
As of Safari 13.1 (released March 24 2020) all modern browsers support ResizeObserver. This is the cleanest way to detect chart resizing.
I recommend
Use a top-down layout such as flexbox or grid to position the divs for your charts
Use ResizeObserver to determine when the div has changed size
Tell the charts to detect the chart div size using .width(null).height(null)
There is currently one resizing example that does this.
The special value null tells the chart to make the SVG node the same size as its parent div:
chart1.width(null)
.height(null)
The callback uses a helper function to disable transitions, because transitions just slow resizing down and make it look clunky:
const callback = chart => entries => {
redraw_chart_no_transitions(
chart
.width(null)
.height(null)
.rescale());
};
Setting up the observer looks like
new ResizeObserver(callback(chart1)).observe(d3.select('#test1').node());
Please see the example for more details.
2. Using window.onresize
The other resizing examples watch for window.onresize because until recently that was the only efficient, reliable cross-browser way to detect changes.
They calculate chart sizes based on the window size, which works well if your layout is bottom-up, e.g. using the default float: left layout.
Here is the function which sets this up:
function apply_resizing(chart, adjustX, adjustY, onresize) {
if(!Array.isArray(chart))
chart = [chart];
if(!isNaN(adjustX))
adjustX = (dx => x => x-dx)(adjustX);
adjustX = adjustX || (x => x);
if(!isNaN(adjustY))
adjustY = (dy => y => y-dy)(adjustY);
adjustY = adjustY || adjustX || (y => y);
chart.forEach(c => c.width(adjustX(window.innerWidth))
.height(adjustY(window.innerHeight)));
window.onresize = function () {
if (onresize) {
chart.forEach(onresize);
}
chart.forEach(c => {
c.width(adjustX(window.innerWidth))
.height(adjustY(window.innerHeight));
if (c.rescale) {
c.rescale();
}
});
redraw_chart_no_transitions(chart);
};
}
A single chart can be initialized like so:
apply_resizing(chart, 20);
This fills the window but makes the chart 20 pixels less wide.
The function can also take multiple charts in an array, and adjustment functions to support complicated layouts, like one where two charts should split the window vertically:
apply_resizing([heatmapChart, barChart], 20, y => y/2-fudge);

Related

How to calculate the total sales and make the displayed totals dynamic?

I need to show the total sales of my data to both sellers and cities. However, the values shown in the totals must be dynamic.
P.S. My table allows the user to select the rows, this is because the values are rendered according to each selection made. (This dynamism between the tables is already working perfectly).
The initial state of the Total is rendered with the general sum of all sellers and cities, below I show the tables with their respective totals, however, as shown in the image, the values are static and not dynamic.
I need it to be diahanic because if I select for example the seller João Luis and Miguel, the value to be shown in the total must be the sum of these two sellers.
I asked a question a while ago about How to add row in datatable - DC.js, a friend (#Gordon) who is helping me a lot in doubts with DC.js and Crossfilter told me to use crossfilter.add(). Unfortunately I couldn't do it the way suggested, so I'm here again with practically the same question (sorry for my stupidity).
var composite = dc.compositeChart('#composite');
var vendedorTable = dc.dataTable("#vendedores");
var citiesTable = dc.dataTable("#cities");
function remove_empty_bins(source_group) {
return {
top: function(N) {
return source_group.all().filter(function(d) {
return d.value.totalAno > 1e-3 ||
d.value.totalHomologo > 1e-3;
}).slice(0, N);
}
};
}
var adjustX = 10,
adjustY = 40;
var url = 'https://gist.githubusercontent.com/bernalvinicius/3cece295bc37de1697e7f83418e7fcc9/raw/a5820379ec6eae76ee792495cc5dd1685c977a73/vendedores.json';
d3.json(url).then(function(data) {
data.forEach(d =>
Object.assign(d, {
mes: d.Month,
atual: d.Vendas_Ano,
passado: d.Vendas_Ant
})
);
var cf = crossfilter(data);
vendedorDim = cf.dimension(function(d) {
return d.vendnm;
});
var vendedorGroup = vendedorDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
citiesDim = cf.dimension(function(d) {
return d.zona;
});
var citiesGroup = citiesDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
var dim = cf.dimension(dc.pluck('mes')),
grp1 = dim.group().reduceSum(dc.pluck('atual')),
grp2 = dim.group().reduceSum(dc.pluck('passado'));
var minMonth = dim.bottom(1)[0].mes;
var maxMonth = dim.top(1)[0].mes;
var all = cf.groupAll();
dc.dataCount(".dc-data-count")
.dimension(cf)
.group(all);
function reduceAdd(p, v) {
p.totalAno += +v.Vendas_Ano;
p.totalHomologo += +v.Vendas_Ant;
return p;
}
function reduceRemove(p, v) {
p.totalAno -= v.Vendas_Ano;
p.totalHomologo -= v.Vendas_Ant;
return p;
}
function reduceInitial() {
return {
totalAno: 0,
totalHomologo: 0,
};
}
// Fake Dimension
rank = function(p) {
return ""
};
// Chart by months
composite
.width(window.innerWidth - adjustX)
.height(300)
.x(d3.scaleLinear().domain([1, 12]))
.yAxisLabel("")
.xAxisLabel("Month")
.legend(dc.legend().x(500).y(0).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.compose([
dc.lineChart(composite)
.dimension(dim)
.colors('steelblue')
.group(grp1, "Currently Year"),
dc.lineChart(composite)
.dimension(dim)
.colors('darkorange')
.group(grp2, "Last Year")
])
.brushOn(true);
composite.brush().extent([-0.5, data.length + 1.5])
composite.extendBrush = function(brushSelection) {
if (brushSelection) {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
const point = Math.round((brushSelection[0] + brushSelection[1]) / 2);
return [
point - 0.5,
point + 0.5
];
}
};
// Sales Table
vendedorTable.width(500)
.height(480)
.dimension(remove_empty_bins(vendedorGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
// Cities Table
citiesTable
.width(500)
.height(480)
.dimension(remove_empty_bins(citiesGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.controlsUseVisibility(true)
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
// Sales click events
vendedorTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
vendedorDim.filter(null);
else
vendedorDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
// Cities click events
citiesTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
citiesDim.filter(null);
else
citiesDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
vendedorTable.filter(null);
vendedorDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
dc.renderAll();
// reset functions
$('#reset').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
$('#resetTable').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
$('#resetTable2').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
/****************************************************************************/
// Functions to handle responsive
apply_resizing(composite, adjustX, adjustY, function(composite) {
composite.legend().x(window.innerWidth - 200);
});
var find_query = function() {
var _map = window.location.search.substr(1).split('&').map(function(a) {
return a.split('=');
}).reduce(function(p, v) {
if (v.length > 1)
p[v[0]] = decodeURIComponent(v[1].replace(/\+/g, " "));
else
p[v[0]] = true;
return p;
}, {});
return function(field) {
return _map[field] || null;
};
}();
var resizeMode = find_query('resize') || 'widhei';
function apply_resizing(composite, adjustX, adjustY, onresize) {
if (resizeMode === 'viewbox') {
composite
.width(300)
.height(200)
.useViewBoxResizing(true);
d3.select(composite.anchor()).classed('fullsize', false);
} else {
adjustX = adjustX || 0;
adjustY = adjustY || adjustX || 0;
composite
.width(window.innerWidth - adjustX)
.height(300);
window.onresize = function() {
if (onresize) {
onresize(composite);
}
composite
.width(window.innerWidth - adjustX)
.height(300);
if (composite.rescale) {
composite.rescale();
}
composite.redraw();
};
}
}
});
#composite {
padding: 10px;
}
.dc-table-group {
visibility: collapse;
}
tr.dc-table-row.sel-rows {
background-color: lightblue;
}
.brush .custom-brush-handle {
display: none;
}
<head>
<!-- favicon -->
<link rel="shortcut icon" href="https://img.icons8.com/nolan/64/puzzle.png">
<!-- bootstrap.css -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- bootstrap-theme.css -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- dc.css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.8/dc.css">
<!-- jquery.js -->
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<!-- bootstrap.js -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- d3.v5.js -->
<script src="https://d3js.org/d3.v5.js"></script>
<!-- crossfilter.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<!-- dc.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.1.8/dc.js"></script>
<title>Chart</title>
</head>
<body>
<div id="composite"></div>
<div class="container-fluid">
<div class="row content">
<div class="col-md-12">
<div class="col-md-6">
<table class="table" id="totaisVendedores">
<thead>
<tr>
<th>Total Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<table class="table" id="totaisCities">
<thead>
<tr>
<th>Total City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<table class="table" id="vendedores">
<thead>
<tr>
<th>Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<table class="table" id="cities">
<thead>
<tr>
<th>City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</body>
Sorry, I completely misread your last question. It was nice of you to accept the answer, but you really didn't have to do that. :)
In order to add totals, you can add a tfoot section to your table
<tfoot>
<tr>
<th>TOTAL</th>
<th id="total-current-year"></th>
<th id="total-last-year"></th>
</tr>
</tfoot>
and drive it with a NumberDisplay widget.
const totalCYDisplay = dc.numberDisplay('#total-current-year')
.group(totalAll)
.valueAccessor(d => d.totalAno)
The default formatting is .2s but numberDisplay accepts any d3-format instance.
If you don't like the animation, you can set .transitionDuration(0)
var composite = dc.compositeChart('#composite');
var vendedorTable = dc.dataTable("#vendedores");
var citiesTable = dc.dataTable("#cities");
function remove_empty_bins(source_group) {
return {
top: function(N) {
return source_group.all().filter(function(d) {
return d.value.totalAno > 1e-3 ||
d.value.totalHomologo > 1e-3;
}).slice(0, N);
}
};
}
var adjustX = 10,
adjustY = 40;
var url = 'https://gist.githubusercontent.com/bernalvinicius/3cece295bc37de1697e7f83418e7fcc9/raw/a5820379ec6eae76ee792495cc5dd1685c977a73/vendedores.json';
d3.json(url).then(function(data) {
data.forEach(d =>
Object.assign(d, {
mes: d.Month,
atual: d.Vendas_Ano,
passado: d.Vendas_Ant
})
);
var cf = crossfilter(data);
vendedorDim = cf.dimension(function(d) {
return d.vendnm;
});
var vendedorGroup = vendedorDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
citiesDim = cf.dimension(function(d) {
return d.zona;
});
var citiesGroup = citiesDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
const totalAll = cf.groupAll()
.reduce(reduceAdd, reduceRemove, reduceInitial);
debugger;
var dim = cf.dimension(dc.pluck('mes')),
grp1 = dim.group().reduceSum(dc.pluck('atual')),
grp2 = dim.group().reduceSum(dc.pluck('passado'));
var minMonth = dim.bottom(1)[0].mes;
var maxMonth = dim.top(1)[0].mes;
var all = cf.groupAll();
dc.dataCount(".dc-data-count")
.dimension(cf)
.group(all);
function reduceAdd(p, v) {
p.totalAno += +v.Vendas_Ano;
p.totalHomologo += +v.Vendas_Ant;
return p;
}
function reduceRemove(p, v) {
p.totalAno -= v.Vendas_Ano;
p.totalHomologo -= v.Vendas_Ant;
return p;
}
function reduceInitial() {
return {
totalAno: 0,
totalHomologo: 0,
};
}
// Fake Dimension
rank = function(p) {
return ""
};
// Chart by months
composite
.width(window.innerWidth - adjustX)
.height(300)
.x(d3.scaleLinear().domain([1, 12]))
.yAxisLabel("")
.xAxisLabel("Month")
.legend(dc.legend().x(500).y(0).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.compose([
dc.lineChart(composite)
.dimension(dim)
.colors('steelblue')
.group(grp1, "Currently Year"),
dc.lineChart(composite)
.dimension(dim)
.colors('darkorange')
.group(grp2, "Last Year")
])
.brushOn(true);
composite.brush().extent([-0.5, data.length + 1.5])
composite.extendBrush = function(brushSelection) {
if (brushSelection) {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
const point = Math.round((brushSelection[0] + brushSelection[1]) / 2);
return [
point - 0.5,
point + 0.5
];
}
};
// Sales Table
vendedorTable.width(500)
.height(480)
.dimension(remove_empty_bins(vendedorGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
// vendedors totals
const totalCYDisplay = dc.numberDisplay('#total-current-year')
.group(totalAll)
.valueAccessor(d => d.totalAno)
const totalLYDisplay = dc.numberDisplay('#total-last-year')
.group(totalAll)
.valueAccessor(d => d.totalHomologo)
// Cities Table
citiesTable
.width(500)
.height(480)
.dimension(remove_empty_bins(citiesGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.controlsUseVisibility(true)
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
// Sales click events
vendedorTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
vendedorDim.filter(null);
else
vendedorDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
// Cities click events
citiesTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
citiesDim.filter(null);
else
citiesDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
vendedorTable.filter(null);
vendedorDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
dc.renderAll();
// reset functions
$('#reset').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
$('#resetTable').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
$('#resetTable2').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
/****************************************************************************/
// Functions to handle responsive
apply_resizing(composite, adjustX, adjustY, function(composite) {
composite.legend().x(window.innerWidth - 200);
});
var find_query = function() {
var _map = window.location.search.substr(1).split('&').map(function(a) {
return a.split('=');
}).reduce(function(p, v) {
if (v.length > 1)
p[v[0]] = decodeURIComponent(v[1].replace(/\+/g, " "));
else
p[v[0]] = true;
return p;
}, {});
return function(field) {
return _map[field] || null;
};
}();
var resizeMode = find_query('resize') || 'widhei';
function apply_resizing(composite, adjustX, adjustY, onresize) {
if (resizeMode === 'viewbox') {
composite
.width(300)
.height(200)
.useViewBoxResizing(true);
d3.select(composite.anchor()).classed('fullsize', false);
} else {
adjustX = adjustX || 0;
adjustY = adjustY || adjustX || 0;
composite
.width(window.innerWidth - adjustX)
.height(300);
window.onresize = function() {
if (onresize) {
onresize(composite);
}
composite
.width(window.innerWidth - adjustX)
.height(300);
if (composite.rescale) {
composite.rescale();
}
composite.redraw();
};
}
}
});
#composite {
padding: 10px;
}
.dc-table-group {
visibility: collapse;
}
tr.dc-table-row.sel-rows {
background-color: lightblue;
}
.brush .custom-brush-handle {
display: none;
}
<head>
<!-- favicon -->
<link rel="shortcut icon" href="https://img.icons8.com/nolan/64/puzzle.png">
<!-- bootstrap.css -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- bootstrap-theme.css -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- dc.css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.8/dc.css">
<!-- jquery.js -->
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<!-- bootstrap.js -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- d3.v5.js -->
<script src="https://d3js.org/d3.v5.js"></script>
<!-- crossfilter.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<!-- dc.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.1.8/dc.js"></script>
<title>Chart</title>
</head>
<body>
<div id="composite"></div>
<div class="container-fluid">
<div class="row content">
<div class="col-md-12">
<div class="col-md-6">
<table class="table" id="totaisVendedores">
<thead>
<tr>
<th>Total Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<table class="table" id="totaisCities">
<thead>
<tr>
<th>Total City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<table class="table" id="vendedores">
<thead>
<tr>
<th>Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
<tfoot>
<tr>
<th>TOTAL</th>
<th id="total-current-year"></th>
<th id="total-last-year"></th>
</tr>
</tfoot>
</table>
</div>
<div class="col-md-6">
<table class="table" id="cities">
<thead>
<tr>
<th>City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</body>

How to remove empty rows in tables when they are zero, and how to allow selection in only one table at a time?

I created two tables that relate. When I select one or more items from Table A, Table B is resized according to that selection and vice versa.
However, I need to lock the selection of the two tables at the same time. For example, if I click on the sellers Miguel andSede, in Table B the values and cities corresponding to these two sellers will be rendered. And if in Table B I click on the cities of Faro andLoulé, automatically the sellers Miguel andSede should be deselected.
What happens at the moment is that I can make selections in both tables at the same time, as shown in the image below:
I need to create a rule where if I have a selection in Table A and click on Table B. The line selected in Table A has no background color and vice versa.
When I make a selection, some lines are empty, but they still appear, as shown in the image below:
How can I prevent these lines from appearing if there is no selection?
I apologize for asking two questions at the same time, as it is the same code I have found better to use here.
Here's my code, thanks in advance.
var vendedorTable = dc.dataTable("#Vendedores");
var citiesTable = dc.dataTable("#cities");
var url = 'https://gist.githubusercontent.com/bernalvinicius/3cece295bc37de1697e7f83418e7fcc9/raw/a5820379ec6eae76ee792495cc5dd1685c977a73/vendedores.json';
d3.json(url).then(function(data) {
data.forEach(function(d) {
var myCrossfilter = crossfilter(data);
var all = myCrossfilter.groupAll();
dc.dataCount(".dc-data-count")
.dimension(myCrossfilter)
.group(all);
vendedorDim = myCrossfilter.dimension(function(d) {
return d.vendnm;
});
var vendedorGroup = vendedorDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
citiesDim = myCrossfilter.dimension(function(d) {
return d.zona;
});
var citiesGroup = citiesDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
function reduceAdd(p, v) {
p.totalAno += +v.Vendas_Ano;
p.totalHomologo += +v.Vendas_Ant;
return p;
}
function reduceRemove(p, v) {
p.totalAno -= v.Vendas_Ano;
p.totalHomologo -= v.Vendas_Ant;
return p;
}
function reduceInitial() {
return {
totalAno: 0,
totalHomologo: 0,
};
}
//Fake Dimension
rank = function(p) {
return ""
};
function checkRows(d) {
if (d.value.totalAno <= 0 || isNaN(d.value.totalAno) || d.value.totalHomologo <= 0 || isNaN(d.value.totalHomologo)) {
return 0;
}
return d;
}
//vendedorTable
vendedorTable.width(500)
.height(480)
.dimension(vendedorGroup)
.group(rank)
.columns([function(d) {
d = checkRows(d);
while (d != 0) {
return d.key;
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
//CityTable
citiesTable.width(500)
.height(480)
.dimension(citiesGroup)
.group(rank)
.columns([function(d) {
d = checkRows(d);
while (d != 0) {
return d.key;
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
vendedorTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
vendedorDim.filter(null);
else
vendedorDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
citiesTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
citiesDim.filter(null);
else
citiesDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
dc.renderAll();
});
$('#reset').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
dc.redrawAll();
});
$('#resetTable').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
dc.redrawAll();
});
$('#resetTable2').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
dc.redrawAll();
});
});
<head>
<style>
.dc-table-group {
visibility: collapse;
}
tr.dc-table-row.sel-rows {
background-color: lightblue;
}
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.8/dc.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<script src="https://d3js.org/d3.v5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.1.8/dc.js"></script>
<title>Vendedores</title>
</head>
<body>
<div class="container-fluid">
<div class="row content">
<div class="col-md-10" style="padding-left: 20px;">
<div class="row marginClass">
<h4 class="pull-left" id="Introduction"><small>Dados fictícios da empresa | Exemplo de Pesquisa
Detalhada |
</small></h4>
<h6 class="dc-data-count" style="float: left;margin-left:5px;">
<span>
<span class="filter-count"></span> selecionado dentre
<span class="total-count"></span> registros |
<a id="reset"> Reset All </a>
</span>
</h6>
</div>
<div class="col-md-6">
<br>
<a id="resetTable"> Reset</a>
<table class="table" id="Vendedores">
<thead>
<tr>
<th>Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<br>
<a id="resetTable2"> Reset</a>
<table class="table" id="cities">
<thead>
<tr>
<th>City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</body>
I'm not sure that I understand the first question.
As for the second question, currently you are filtering out the data inside of the formatting functions for the table:
.columns([function(d) {
d = checkRows(d);
while (d != 0) {
return d.key;
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
}
])
That works, but it's a weird way to go about it for two reasons:
while(...) means "repeat until false". if(...) would be more idiomatic here.
More importantly, this still prints the rows, but if the values are zero or NaN, it prints undefined (the default return value for a function in JavaScript) and this shows up as an empty cell.
It would be better to filter the data before it gets to the table. You can do this using a fake group. Specifically, you can create an object which supports the .top(N) function that the table will use to pull data, but skips any rows where either of these values is close to zero:
function remove_empty_bins(source_group) {
return {
top: function (N) {
return source_group.all().filter(function(d) {
return d.value.totalAno > 1e-3 &&
d.value.totalHomologo > 1e-3; // does it have a value?
}).slice(0, N);
}
};
}
Apply this function to each of your groups:
vendedorTable.width(500)
.height(480)
.dimension(remove_empty_bins(vendedorGroup))
citiesTable.width(500)
.height(480)
.dimension(remove_empty_bins(citiesGroup))
And now your formatting functions can stick to formatting!
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
var vendedorTable = dc.dataTable("#Vendedores");
var citiesTable = dc.dataTable("#cities");
function remove_empty_bins(source_group) {
return {
top: function (N) {
return source_group.all().filter(function(d) {
return d.value.totalAno > 1e-3 &&
d.value.totalHomologo > 1e-3; // does it have a value?
}).slice(0, N);
}
};
}
var url = 'https://gist.githubusercontent.com/bernalvinicius/3cece295bc37de1697e7f83418e7fcc9/raw/a5820379ec6eae76ee792495cc5dd1685c977a73/vendedores.json';
d3.json(url).then(function(data) {
data.forEach(function(d) {
var myCrossfilter = crossfilter(data);
var all = myCrossfilter.groupAll();
dc.dataCount(".dc-data-count")
.dimension(myCrossfilter)
.group(all);
vendedorDim = myCrossfilter.dimension(function(d) {
return d.vendnm;
});
var vendedorGroup = vendedorDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
citiesDim = myCrossfilter.dimension(function(d) {
return d.zona;
});
var citiesGroup = citiesDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
function reduceAdd(p, v) {
p.totalAno += +v.Vendas_Ano;
p.totalHomologo += +v.Vendas_Ant;
return p;
}
function reduceRemove(p, v) {
p.totalAno -= v.Vendas_Ano;
p.totalHomologo -= v.Vendas_Ant;
return p;
}
function reduceInitial() {
return {
totalAno: 0,
totalHomologo: 0,
};
}
//Fake Dimension
rank = function(p) {
return ""
};
function checkRows(d) {
if (d.value.totalAno <= 0 || isNaN(d.value.totalAno) || d.value.totalHomologo <= 0 || isNaN(d.value.totalHomologo)) {
return 0;
}
return d;
}
//vendedorTable
vendedorTable.width(500)
.height(480)
.dimension(remove_empty_bins(vendedorGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
//CityTable
citiesTable.width(500)
.height(480)
.dimension(remove_empty_bins(citiesGroup))
.group(rank)
.columns([function(d) {
d = checkRows(d);
while (d != 0) {
return d.key;
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
vendedorTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
vendedorDim.filter(null);
else
vendedorDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
citiesTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
citiesDim.filter(null);
else
citiesDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
dc.renderAll();
});
$('#reset').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
dc.redrawAll();
});
$('#resetTable').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
dc.redrawAll();
});
$('#resetTable2').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
dc.redrawAll();
});
});
<head>
<style>
.dc-table-group {
visibility: collapse;
}
tr.dc-table-row.sel-rows {
background-color: lightblue;
}
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.8/dc.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<script src="https://d3js.org/d3.v5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.1.8/dc.js"></script>
<title>Vendedores</title>
</head>
<body>
<div class="container-fluid">
<div class="row content">
<div class="col-md-10" style="padding-left: 20px;">
<div class="row marginClass">
<h4 class="pull-left" id="Introduction"><small>Dados fictícios da empresa | Exemplo de Pesquisa
Detalhada |
</small></h4>
<h6 class="dc-data-count" style="float: left;margin-left:5px;">
<span>
<span class="filter-count"></span> selecionado dentre
<span class="total-count"></span> registros |
<a id="reset"> Reset All </a>
</span>
</h6>
</div>
<div class="col-md-6">
<br>
<a id="resetTable"> Reset</a>
<table class="table" id="Vendedores">
<thead>
<tr>
<th>Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<br>
<a id="resetTable2"> Reset</a>
<table class="table" id="cities">
<thead>
<tr>
<th>City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</body>

Average in Crossfilter and DC.js

In my application, I have two charts and one table. The charts names are sellerChart and dateUnitPriceChart and table name is datatable.For sellerChart, I create dimension like this and then draw a chart :
var sellerDimension = data.dimension(function (d) {
return d.slr_Name;
});
var sellerGroup = sellerDimension.group().reduceSum(function (d) {
return d.fdl_Num;
});
As you see is use reduceSum and this is what I want but for another chart, I want to use average but currently, it uses reduceSum like this
var dateDimension = data.dimension(function (d) {
return d.fct_Date;
});
var unitPriceGroup = dateDimension.group().reduceSum(function
(d) {
return d.fdl_UniyPrice;
});
for showing average I try to use reductio like this :
var unitPriceGroup = dateDimension.group()
var reducer = reductio()
.count(true)
.sum(function (d) {
return d.fdl_UniyPrice;
})
.avg(true);
reducer(unitPriceGroup);
Now I have averaged in reducer but the chart show only X and Y axis without any information, so my question is how to show average based on the average of fdl_UniyPrice.
I create a JS Fiddle, my codes start from line 43 and my data start on line 42
While looking at your data and based on your explanation, avg calculation can be achieved using custom reduce function. By using the dateDimension to create grouping function to get the average. So regardless of the slr_Name and mrq_Requester_dep_title, It will create group for each date with sum up the fdl_UniyPrice.
var dateDimension = data.dimension(function (d) {
return d.fct_Date;
});
var unitPriceGroup = dateDimension.group().reduce(
//return d.fdl_UniyPrice;
//add
function (p, v) {
++p.count;
p.total += v.fdl_UniyPrice;
if (p.count == 0) {
p.average = 0;
} else {
p.average = p.total / p.count;
}
return p;
},
// remove
function (p, v) {
--p.count;
p.total -= v.fdl_UniyPrice;
if (p.count == 0) {
p.average = 0;
} else {
p.average = p.total / p.count;
}
return p;
},
// initial
function () {
return {
count: 0,
total: 0,
average: 0
};
}
);
var myData = [{"slr_Name":"handStore","fct_Date":"10/31/2016","fdl_UniyPrice":40000,"fdl_TotalPrice":2400000,"ShamsiDate":"1395/08/10","mrq_No":"335","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"boss office","fdl_Num":60},{"slr_Name":"Sh Store","fct_Date":"12/12/2016","fdl_UniyPrice":50000,"fdl_TotalPrice":60000,"ShamsiDate":"1395/09/22","mrq_No":"357","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"Start Up","fdl_Num":1.2},{"slr_Name":"handStore","fct_Date":"12/18/2016","fdl_UniyPrice":44000,"fdl_TotalPrice":96800,"ShamsiDate":"1395/09/28","mrq_No":"344","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"Select Office","fdl_Num":2.2},{"slr_Name":"handStore","fct_Date":"12/18/2016","fdl_UniyPrice":40000,"fdl_TotalPrice":1240000,"ShamsiDate":"1395/09/28","mrq_No":"326","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"U Office","fdl_Num":31},{"slr_Name":"handStore","fct_Date":"12/20/2016","fdl_UniyPrice":42000,"fdl_TotalPrice":2142000,"ShamsiDate":"1395/09/30","mrq_No":"340","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"MH","fdl_Num":51},{"slr_Name":"N Brothers","fct_Date":"12/21/2016","fdl_UniyPrice":38000,"fdl_TotalPrice":1140000,"ShamsiDate":"1395/10/01","mrq_No":"440","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"Start Up","fdl_Num":30},{"slr_Name":"N Brothers","fct_Date":"12/21/2016","fdl_UniyPrice":38000,"fdl_TotalPrice":2432000,"ShamsiDate":"1395/10/01","mrq_No":"423","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"U Office","fdl_Num":64},{"slr_Name":"N Brothers","fct_Date":"12/26/2016","fdl_UniyPrice":38000,"fdl_TotalPrice":1710000,"ShamsiDate":"1395/10/06","mrq_No":"420","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"Managements","fdl_Num":45},{"slr_Name":"handStore","fct_Date":"12/27/2016","fdl_UniyPrice":44000,"fdl_TotalPrice":1320000,"ShamsiDate":"1395/10/07","mrq_No":"345","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"Finance","fdl_Num":30},{"slr_Name":"handStore","fct_Date":"12/27/2016","fdl_UniyPrice":40000,"fdl_TotalPrice":280000,"ShamsiDate":"1395/10/07","mrq_No":"337","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"Manage and create","fdl_Num":7},{"slr_Name":"Store 2","fct_Date":"12/28/2016","fdl_UniyPrice":38000,"fdl_TotalPrice":874000,"ShamsiDate":"1395/10/08","mrq_No":"424","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"Start Up","fdl_Num":23},{"slr_Name":"handStore","fct_Date":"1/8/2017","fdl_UniyPrice":44000,"fdl_TotalPrice":264000,"ShamsiDate":"1395/10/19","mrq_No":"425","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"human resource","fdl_Num":6},{"slr_Name":"handStore","fct_Date":"2/21/2017","fdl_UniyPrice":47000,"fdl_TotalPrice":117500,"ShamsiDate":"1395/12/03","mrq_No":"553","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"Doctors","fdl_Num":2.5},{"slr_Name":"handStore","fct_Date":"3/3/2017","fdl_UniyPrice":55000,"fdl_TotalPrice":3300000,"ShamsiDate":"1395/12/13","mrq_No":"577","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"Human2","fdl_Num":60},{"slr_Name":"handStore","fct_Date":"3/26/2017","fdl_UniyPrice":70000,"fdl_TotalPrice":252000,"ShamsiDate":"1396/01/06","mrq_No":"1","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"boss office","fdl_Num":3.6},{"slr_Name":"B Store","fct_Date":"5/2/2017","fdl_UniyPrice":68000,"fdl_TotalPrice":89760,"ShamsiDate":"1396/02/12","mrq_No":"324","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"boss office","fdl_Num":1.32},{"slr_Name":"Y Store","fct_Date":"5/7/2017","fdl_UniyPrice":40000,"fdl_TotalPrice":1360000,"ShamsiDate":"1396/02/17","mrq_No":"303","drq_SaleSplName":"AZ","mrq_Requester_dep_title":"D & F Manage","fdl_Num":34},{"slr_Name":"handStore","fct_Date":"5/9/2017","fdl_UniyPrice":53600,"fdl_TotalPrice":268000,"ShamsiDate":"1396/02/19","mrq_No":"866","drq_SaleSplName":"AZ","mrq_Requester_dep_title":"manage office","fdl_Num":5},{"slr_Name":"Y Store","fct_Date":"5/14/2017","fdl_UniyPrice":70000,"fdl_TotalPrice":1062600,"ShamsiDate":"1396/02/24","mrq_No":"445","drq_SaleSplName":"ES Ja","mrq_Requester_dep_title":"Wom","fdl_Num":15.18},{"slr_Name":"handStore","fct_Date":"5/24/2017","fdl_UniyPrice":50000,"fdl_TotalPrice":300000,"ShamsiDate":"1396/03/03","mrq_No":"866","drq_SaleSplName":"AZ","mrq_Requester_dep_title":"manage office","fdl_Num":6},{"slr_Name":"Y Store","fct_Date":"5/29/2017","fdl_UniyPrice":50000,"fdl_TotalPrice":920000,"ShamsiDate":"1396/03/08","mrq_No":"866","drq_SaleSplName":"AZ","mrq_Requester_dep_title":"manage office","fdl_Num":18.4},{"slr_Name":"Y Store","fct_Date":"8/19/2017","fdl_UniyPrice":37000,"fdl_TotalPrice":370000,"ShamsiDate":"1396/05/28","mrq_No":"2496","drq_SaleSplName":"AZ","mrq_Requester_dep_title":"D & F Manage","fdl_Num":10},{"slr_Name":"Sh Store","fct_Date":"9/4/2017","fdl_UniyPrice":65000,"fdl_TotalPrice":195000,"ShamsiDate":"1396/06/13","mrq_No":"2885","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"D Center","fdl_Num":3},{"slr_Name":"Y Store","fct_Date":"10/9/2017","fdl_UniyPrice":40000,"fdl_TotalPrice":800000,"ShamsiDate":"1396/07/17","mrq_No":"3753","drq_SaleSplName":"AZ","mrq_Requester_dep_title":"D & F Manage","fdl_Num":20},{"slr_Name":"handStore","fct_Date":"10/19/2017","fdl_UniyPrice":40000,"fdl_TotalPrice":520000,"ShamsiDate":"1396/07/27","mrq_No":"4068","drq_SaleSplName":"F P","mrq_Requester_dep_title":"MH","fdl_Num":13},{"slr_Name":"Seasson Store","fct_Date":"10/21/2017","fdl_UniyPrice":35000,"fdl_TotalPrice":4060000,"ShamsiDate":"1396/07/29","mrq_No":"4068","drq_SaleSplName":"F P","mrq_Requester_dep_title":"MH","fdl_Num":116},{"slr_Name":"Seasson Store","fct_Date":"10/21/2017","fdl_UniyPrice":29000,"fdl_TotalPrice":2610000,"ShamsiDate":"1396/07/29","mrq_No":"3898","drq_SaleSplName":"F P","mrq_Requester_dep_title":"MH","fdl_Num":90},{"slr_Name":"Seasson Store","fct_Date":"10/22/2017","fdl_UniyPrice":30000,"fdl_TotalPrice":180000,"ShamsiDate":"1396/07/30","mrq_No":"4068","drq_SaleSplName":"F P","mrq_Requester_dep_title":"MH","fdl_Num":6},{"slr_Name":"Aba Store","fct_Date":"10/23/2017","fdl_UniyPrice":30000,"fdl_TotalPrice":300000,"ShamsiDate":"1396/08/01","mrq_No":"1","drq_SaleSplName":"","mrq_Requester_dep_title":"Store","fdl_Num":10},{"slr_Name":"Aba Store","fct_Date":"4/8/2018","fdl_UniyPrice":32000,"fdl_TotalPrice":224000,"ShamsiDate":"1397/01/19","mrq_No":"4142","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"EST","fdl_Num":7},{"slr_Name":"Hob Store","fct_Date":"5/12/2018","fdl_UniyPrice":32000,"fdl_TotalPrice":96000,"ShamsiDate":"1397/02/22","mrq_No":"2","drq_SaleSplName":"MBA","mrq_Requester_dep_title":"Center Off","fdl_Num":3}];
//var parseDate = d3.timeFormat("%m %d %Y").parse;
myData.forEach(function (d) {
var parseDate = d3.timeParse("%m/%d/%Y")
d.fct_Date = parseDate(d.fct_Date);
/* var newD = new Date(d.fct_Date);
var jdate3 = new JDate(newD); */
formatYear = d3.timeFormat("%Y");
d.year = formatYear(d.fct_Date);
});
var data = crossfilter(myData);
var sellers = [];
for (var d in myData) {
if (sellers.indexOf(myData[d].slr_Name) == -1) {
sellers.push(myData[d].slr_Name);
}
}
// var colorScale = d3.scale.ordinal().range(['#DB0A5B', '#F62459', '#F1A9A0', '#2C3E50', '#26A65B', '#E87E04', '#D35400']);
var sellerDimension = data.dimension(function (d) {
return d.slr_Name;
});
var sellerGroup = sellerDimension.group().reduceSum(function (d) {
return d.fdl_Num;
});
var sellerChart = dc.barChart('#sellerChart');
sellerChart
.height(300)
.width(700)
.dimension(sellerDimension)
.group(sellerGroup)
.x(d3.scaleBand())
.xUnits(dc.units.ordinal)
.xAxisLabel('Sellers')
.yAxisLabel('Count')
.elasticX(true)
.elasticY(true)
.xAxis().ticks(3);
sellerChart.title(function (d) {
return d.key + ' : ' + d.value;
});
// sellerChart.xAxis().tickValues([]);
// sellerChart.colors(d3.scale.ordinal().range(colorScale));
/**************************************************/
/* Data Unit Price Chart
/**************************************************/
var dateDimension = data.dimension(function (d) {
return d.fct_Date;
});
var unitPriceGroup = dateDimension.group().reduce(
//return d.fdl_UniyPrice;
//add
function (p, v) {
++p.count;
p.total += v.fdl_UniyPrice;
if (p.count == 0) {
p.average = 0;
} else {
p.average = p.total / p.count;
}
return p;
},
// remove
function (p, v) {
--p.count;
p.total -= v.fdl_UniyPrice;
if (p.count == 0) {
p.average = 0;
} else {
p.average = p.total / p.count;
}
return p;
},
// initial
function () {
return {
count: 0,
total: 0,
average: 0
};
}
);
var formatTime = d3.timeFormat("%B %d, %Y");
var minDate = formatTime(dateDimension.bottom(1)[0].fct_Date);
var maxDate = formatTime(dateDimension.top(1)[0].fct_Date);
var dateUnitPriceChart = dc.lineChart('#dateunitprice');
dateUnitPriceChart
.height(250)
.width(700)
.curve(d3.curveStepAfter)
.renderArea(true)
.dimension(dateDimension)
.group(unitPriceGroup)
.brushOn(true)
.elasticX(true)
.elasticY(true)
.valueAccessor(function (d) {
return d.value.average;
})
.x(d3.scaleTime());
/* dateUnitPriceChart.xAxis().tickFormat(function (val) {
console.log(val);
var newD = new Date(val);
var jdate3 = new JDate(newD);
if (jdate3.getMonth() === 1) {
return jdate3.format('YYYY'); //return a year in number like 1396
}
return jdate3.format('MMMM'); //return month like azar
}); */
dateUnitPriceChart.margins().left = 70;
/** Create Data Table */
var datatable = dc.dataTable('#data-table');
datatable
.dimension(sellerDimension)
.group(function (d) {
return d.year;
})
.size(10000)
// dynamic columns creation using an array of closures
.columns([
function (d) {
return d.mrq_No
},
function (d) {
return d.mrq_Requester_dep_title
},
function (d) {
return d.drq_SaleSplName
},
function (d) {
return d.slr_Name
},
function (d) {
//return d.fct_Date.getFullYear() + ' ' + d.fct_Date.getMonth() + ' ' + d.fct_Date.getDate();
return d.ShamsiDate;
},
function (d) {
return d.fdl_UniyPrice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); ;
},
function (d) {
return d.fdl_Num;
}
])
.showGroups(false)
.sortBy(function (d) {
return d.slr_Name;
})
.order(d3.ascending);
datatable.title(function (d) {
return d.key + ' : ' + d.value;
});
datatable.ordering(function (d) {
return d.ShamsiDate
})
/***********************/
dc.renderAll();
<link href="https://dc-js.github.io/dc.js/css/dc.css" rel="stylesheet"/>
<script src="https://dc-js.github.io/dc.js/js/crossfilter.js"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://dc-js.github.io/dc.js/js/dc.js"></script>
</script>
<div style="margin-top: -50px;">
<div class="divForms" style="border: solid; border-color: gray; border-radius: 5px; border-width: 1px; text-align:center; padding-left:20px;">
<div style="margin-bottom: 20px; width: 100%;">
<div style="text-align: right; padding-right: 20px; float: right; width: 44%;">
<a id="lnk_Reset" href="javascript:dc.filterAll(); dc.renderAll();">Reset</a>
</div>
<div style="text-align: center; float: right;">
<span>کالا : </span>
<b>
<asp:Label ID="lbl_Good" runat="server" Text=""></asp:Label></b>
</div>
</div>
<div id="sellerChart" style="direction: ltr !important"></div>
<div id="dateunitprice" style="direction: ltr !important"></div>
<table class="table table-condensed table-striped table-bordered table-hover no-margin" border="1" id="data-table" style="width: 100%; border-collapse: collapse; margin-left:5px;">
<thead>
<tr class="HeaderStyle" style="color: #6484E5;">
<th class="text-center" scope="col">Num Request</th>
<th class="text-center" scope="col">Unit Requested</th>
<th class="text-center" scope="col">User</th>
<th class="text-center" scope="col">Seller</th>
<th class="text-center" scope="col">Date</th>
<th class="text-center" scope="col">Unit Price</th>
<th class="text-center" scope="col">Count</th>
</tr>
</thead>
</table>
</div>
</div>
Build with latest version of dc and d3
Bar chart replaced with line chart (Get narrow line for each date)

jquery mobile d3 chart works in chrome, not in firefox

Puzzling problem:
My d3 chart, which has a day selector (right arrow) works in a stand-alone file. By "works" I mean when you click on the right arrow it advances through the data array and the chart bars update.
However, inside a jquery mobile multipage document, it won't work in Firefox. In jqm multipage it does work in Chrome (linux and windows) and IE.
Briefly, you click on the "chart page," then click on the right arrow to view the data for each day.
Don't worry about the arrow code, I have to fix it. What worries me is that this doesn't work in Firefox.
Here is the fiddle. Try it in those browsers.
html:
<!-- Start of first page: #one -->
<div data-role='page' id='one'>
<div data-role='header'>
<h1>Multi-page</h1>
</div>
<!-- /header -->
<div data-role='content'>
<h2>One</h2>
<p>I have an <code>id</code> of 'one' on my page container. I'm first in the source order so I'm shown when the page loads.</p>
<h3>Show internal pages:</h3>
<p><a href='#snapshot-chart-page' data-role='button'>Chart page</a></p>
</div>
<!-- /content -->
<div data-role='footer' data-theme='d'>
<h4>Page Footer</h4>
</div>
<!-- /footer -->
</div>
<!-- /page one -->
<div data-role='page' id='snapshot-chart-page' data-ajax='false'>
<div data-role='header' style='background:#82ca46;'>
<a href='#nav-panel' data-icon='bars' data-iconpos='notext' class='ui-nodisc-icon' style='background-color: transparent;'>Menu</a>
<div align='center' style='vertical-align:top;'>
<h1>Page title
</h1></div>
<a href='#add-form' data-icon='calendar' data-iconpos='notext' class='ui-nodisc-icon' style='background-color: transparent;'>Add</a>
</div>
<!-- /header -->
<div role='main' class='ui-content'>
<div style='width:100%; margin: 0;padding: 0; overflow: auto;'>
<form style='display: inline-block;
position: relative;
vertical-align: middle;
margin-right: 6px;'>
<input type='button' data-role='button' data-icon='arrow-l' data-iconpos='notext' class='ui-nodisc-icon previous-next-period left-previous select-custom-14' style='background-color: transparent;' field='quantity'>
<input type='text' name='quantity' value='0' class='qty' />
<input type='button' data-role='button' data-icon='arrow-r' data-iconpos='notext' class='ui-nodisc-icon previous-next-period right-next select-custom-14' style='background-color: transparent;' field='quantity'>
</form>
<table data-role='table' data-transition='fade' class='ghg-tables'>
<caption class='barchart_title tabletitle'></caption>
<thead>
</thead>
</table>
<div class='chart-here'></div>
</div>
</div>
<!-- /main -->
</div>
<!-- /snapshot-chart-page -->
js:
(function() {
var margin = {
top: 20,
right: 20,
bottom: 40,
left: 80
},
width = 340 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1, 1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
var svg = d3.select('.chart-here').append('svg')
.attr('viewBox', '0 0 340 250')
.attr('preserveAspectRatio', 'xMinYMin meet')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left / 1.5 + ',' + margin.top / 1.5 + ')');
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
.ticks(5)
.outerTickSize(0)
.tickFormat(d3.format(',.3s'));
var barchart_i = 0;
var arr1 = [{
"period": "day",
"allcars_title": "Mileage by car, on Tuesday, March 01 2016. Total: 454.95M mi.",
"car": [{
"Chevvy": 33733000
}, {
"Ford": 32633000
}, {
"Honda": 119182000
}, {
"Tesla": 614000
}, {
"Audi": 268292000
}, {
"Hummer": 493000
}]
}, {
"period": "day",
"allcars_title": "Mileage by car, on Wednesday, March 02 2016. Total: 457.26M mi.",
"car": [{
"Chevvy": 23052000
}, {
"Ford": 44630000
}, {
"Honda": 121635000
}, {
"Tesla": 2599000
}, {
"Audi": 264247000
}, {
"Hummer": 1100000
}]
}, {
"period": "day",
"allcars_title": "Mileage by car, on Thursday, March 03 2016. Total: 452.96M mi.",
"car": [{
"Chevvy": 8577000
}, {
"Ford": 54172000
}, {
"Honda": 121661000
}, {
"Tesla": 1975000
}, {
"Audi": 265483000
}, {
"Hummer": 1089000
}]
}];
var period_grain = arr1[0].period;
var allcars_hour = arr1[barchart_i];
var car = allcars_hour.car;
var allcars_dash_title = allcars_hour.allcars_title;
jQuery('.barchart_title').text(allcars_dash_title);
var newobj = [];
for (var allcars_hourx1 = 0; allcars_hourx1 < car.length; allcars_hourx1++) {
var xx = car[allcars_hourx1];
for (var value in xx) {
var chartvar = newobj.push({
car: value,
miles: xx[value]
});
var data = newobj;
data = data.sort(function(a, b) {
return b.miles - a.miles;
});
}
}
x.domain(data.map(function(d) {
return d.car;
}));
if (period_grain == 'hour') {
var staticMax = 13000000;
}
if (period_grain == 'day') {
var staticMax = 300000000;
}
if (period_grain == 'month') {
var staticMax = 2000000;
}
if (period_grain == 'year') {
var staticMax = 35000000;
}
y.domain([0, d3.max(data, function(d) {
return d.miles > staticMax ? d.miles : staticMax;
})]);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis)
.append('text')
.attr('y', 6)
.attr('dy', '.71em')
.style('text-anchor', 'start');
var changeHour = function() {
var dataJoin = svg.selectAll('.bar')
.data(data, function(d) {
return d.car;
});
svg.selectAll('.y.axis')
.call(yAxis);
xtext = svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(-20,' + height + ')') /*move tick text so it aligns with rects*/
.call(xAxis)
/* possible new elements, fired first time, set non-data dependent attributes*/
dataJoin
.enter()
.append('rect')
.attr('class', 'bar')
.attr('transform', 'translate(-20)') /*move rects closer to Y axis*/
/* changes to existing elements (now including the newly appended elements from above) which depend on data values (d)*/
dataJoin
.attr('x', function(d) {
return x(d.car);
})
.attr('width', x.rangeBand() * 1)
.attr('y', function(d) {
return y(d.miles);
})
.attr('height', function(d) {
return height - y(d.miles);
})
.style('fill', function(d) {
if (d.car == 'Audi' || d.car == 'Chevvy' || d.car == 'Honda' || d.car == 'Hummer') {
return 'green';
} else {
return '#404040';
}
})
xtext.selectAll('text')
.attr('transform', function(d) {
return 'translate(' + this.getBBox().height * 50 + ',' + this.getBBox().height + ')rotate(0)';
});
dataJoin.exit().remove();
}
changeHour();
//function to allow user to click arrows to view next/previous period_grain
// This button will increment the value
$('.right-next').click(function(e) {
// Stop acting like a button
e.preventDefault();
// Get the field name
fieldName = $(this).attr('field');
// Get its current value
barchart_i = parseInt($('input[name=' + fieldName + ']').val());
// If is not undefined
if (!isNaN(barchart_i)) {
// Increment
$('input[name=' + fieldName + ']').val(barchart_i + 1);
} else {
// Otherwise set to 0
$('input[name=' + fieldName + ']').val(0);
}
incrementHour();
});
// This button will decrement the value till 0
$('.left-previous').click(function(e) {
// Stop acting like a button
e.preventDefault();
// Get the field name
fieldName = $(this).attr('field');
// Get its current value
barchart_i = parseInt($('input[name=' + fieldName + ']').val());
// If it isn't undefined or if it is greater than 0
if (!isNaN(barchart_i) && barchart_i > 0) {
// Decrement one
$('input[name=' + fieldName + ']').val(barchart_i - 1);
} else {
// Otherwise set to 0
$('input[name=' + fieldName + ']').val(0);
}
incrementHour();
});
incrementHour = function() {
allcars_hour = arr1[barchart_i];
var car = allcars_hour.car;
var allcars_dash_title = allcars_hour.allcars_title;
var newobj = [];
for (var allcars_hourx1 = 0; allcars_hourx1 < car.length; allcars_hourx1++) {
var xx = car[allcars_hourx1];
for (var value in xx) {
var chartvar = newobj.push({
car: value,
miles: xx[value]
});
}
}
data = newobj;
console.log('data is ' + data);
data = data.sort(function(a, b) {
return b.miles - a.miles;
});
x.domain(data.map(function(d) {
return d.car;
}));
y.domain([0, d3.max(data, function(d) {
return d.miles > staticMax ? d.miles : staticMax;
})]);
jQuery('.barchart_title').text(allcars_dash_title);
changeHour();
};
})();
This is bizarre. I have researched this extensively, and cannot find anything that relates to it.
Has anybody else encountered this? If so, are there any ideas how to fix it?

d3.js d3.json is not working - how to make this function work?

Trying to develop a data visualization app with d3.js
Using a local json file named "yelp_test_set_business.json"
When I attempt to use d3.json to load this data, nothing is being passed to the callback function, which, in tern, also triggers an error within crossfilter.js library.
here is my entire file:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<script src='javascript/d3.js' type='text/javascript'></script>
<script src='javascript/crossfilter.js' type='text/javascript'></script>
<script src='javascript/dc.js' type='text/javascript'></script>
<script src='javascript/jquery-1.12.1.min.js' type='text/javascript'></script>
<script type='text/javascript'>
/********************************************************
* *
* dj.js example using Yelp Kaggle Test Dataset *
* Eol 9th May 2013 *
* *
********************************************************/
/********************************************************
* *
* Step0: Load data from json file *
* *
********************************************************/
d3.json("data/yelp_test_set_business.json", function (yelp_data) {
/********************************************************
* *
* Step1: Create the dc.js chart objects & ling to div *
* *
********************************************************/
var bubbleChart = dc.bubbleChart("#dc-bubble-graph");
var pieChart = dc.pieChart("#dc-pie-graph");
var volumeChart = dc.barChart("#dc-volume-chart");
var lineChart = dc.lineChart("#dc-line-chart");
var dataTable = dc.dataTable("#dc-table-graph");
var rowChart = dc.rowChart("#dc-row-graph");
/********************************************************
* *
* Step2: Run data through crossfilter *
* *
********************************************************/
var ndx = crossfilter(yelp_data);
/********************************************************
* *
* Step3: Create Dimension that we'll need *
* *
********************************************************/
// for volumechart
var cityDimension = ndx.dimension(function (d) { return d.city; });
var cityGroup = cityDimension.group();
var cityDimensionGroup = cityDimension.group().reduce(
//add
function(p,v){
++p.count;
p.review_sum += v.review_count;
p.star_sum += v.stars;
p.review_avg = p.review_sum / p.count;
p.star_avg = p.star_sum / p.count;
return p;
},
//remove
function(p,v){
--p.count;
p.review_sum -= v.review_count;
p.star_sum -= v.stars;
p.review_avg = p.review_sum / p.count;
p.star_avg = p.star_sum / p.count;
return p;
},
//init
function(p,v){
return {count:0, review_sum: 0, star_sum: 0, review_avg: 0, star_avg: 0};
}
);
// for pieChart
var startValue = ndx.dimension(function (d) {
return d.stars*1.0;
});
var startValueGroup = startValue.group();
// For datatable
var businessDimension = ndx.dimension(function (d) { return d.business_id; });
/********************************************************
* *
* Step4: Create the Visualisations *
* *
********************************************************/
bubbleChart.width(650)
.height(300)
.dimension(cityDimension)
.group(cityDimensionGroup)
.transitionDuration(1500)
.colors(["#a60000","#ff0000", "#ff4040","#ff7373","#67e667","#39e639","#00cc00"])
.colorDomain([-12000, 12000])
.x(d3.scale.linear().domain([0, 5.5]))
.y(d3.scale.linear().domain([0, 5.5]))
.r(d3.scale.linear().domain([0, 2500]))
.keyAccessor(function (p) {
return p.value.star_avg;
})
.valueAccessor(function (p) {
return p.value.review_avg;
})
.radiusValueAccessor(function (p) {
return p.value.count;
})
.transitionDuration(1500)
.elasticY(true)
.yAxisPadding(1)
.xAxisPadding(1)
.label(function (p) {
return p.key;
})
.renderLabel(true)
.renderlet(function (chart) {
rowChart.filter(chart.filter());
})
.on("postRedraw", function (chart) {
dc.events.trigger(function () {
rowChart.filter(chart.filter());
});
});
;
pieChart.width(200)
.height(200)
.transitionDuration(1500)
.dimension(startValue)
.group(startValueGroup)
.radius(90)
.minAngleForLabel(0)
.label(function(d) { return d.data.key; })
.on("filtered", function (chart) {
dc.events.trigger(function () {
if(chart.filter()) {
console.log(chart.filter());
volumeChart.filter([chart.filter()-.25,chart.filter()-(-0.25)]);
}
else volumeChart.filterAll();
});
});
volumeChart.width(230)
.height(200)
.dimension(startValue)
.group(startValueGroup)
.transitionDuration(1500)
.centerBar(true)
.gap(17)
.x(d3.scale.linear().domain([0.5, 5.5]))
.elasticY(true)
.on("filtered", function (chart) {
dc.events.trigger(function () {
if(chart.filter()) {
console.log(chart.filter());
lineChart.filter(chart.filter());
}
else
{lineChart.filterAll()}
});
})
.xAxis().tickFormat(function(v) {return v;});
console.log(startValueGroup.top(1)[0].value);
lineChart.width(230)
.height(200)
.dimension(startValue)
.group(startValueGroup)
.x(d3.scale.linear().domain([0.5, 5.5]))
.valueAccessor(function(d) {
return d.value;
})
.renderHorizontalGridLines(true)
.elasticY(true)
.xAxis().tickFormat(function(v) {return v;}); ;
rowChart.width(340)
.height(850)
.dimension(cityDimension)
.group(cityGroup)
.renderLabel(true)
.colors(["#a60000","#ff0000", "#ff4040","#ff7373","#67e667","#39e639","#00cc00"])
.colorDomain([0, 0])
.renderlet(function (chart) {
bubbleChart.filter(chart.filter());
})
.on("filtered", function (chart) {
dc.events.trigger(function () {
bubbleChart.filter(chart.filter());
});
});
dataTable.width(800).height(800)
.dimension(businessDimension)
.group(function(d) { return "List of all Selected Businesses"
})
.size(100)
.columns([
function(d) { return d.name; },
function(d) { return d.city; },
function(d) { return d.stars; },
function(d) { return d.review_count; },
function(d) { return 'Map"}
])
.sortBy(function(d){ return d.stars; })
// (optional) sort order, :default ascending
.order(d3.ascending);
/********************************************************
* *
* Step6: Render the Charts *
* *
********************************************************/
dc.renderAll();
});
</script>
<link href='stylesheets/bootstrap.min.css' rel='stylesheet' type='text/css'>
<!--<link href='stylesheets/dc.css' rel='stylesheet' type='text/css'>-->
<!--<script src='simple_vis.js' type='text/javascript'></script>-->
</head>
<body>
<div class='container' id='main-container'>
<div class='content'>
<div class='container' style='font: 10px sans-serif;'>
<h3>Visualisation of Kaggle Yelp Test Business Data set (using dc.js)</h3>
<h4>Demo for the Dublin Data Visualisation Meetup Group</h4>
<div class='row-fluid'>
<div class='remaining-graphs span8'>
<div class='row-fluid'>
<div class='bubble-graph span12' id='dc-bubble-graph'>
<h4>Average Rating (x-axis), Average Number of Reviews (y-axis), Number of Business' (Size)</h4>
</div>
</div>
<div class='row-fluid'>
<div class='pie-graph span4' id='dc-pie-graph'>
<h4>Average Rating in Stars (Pie)</h4>
</div>
<div class='pie-graph span4' id='dc-volume-chart'>
<h4>Average Rating in Stars / Number of Reviews (Bar)</h4>
</div>
<div class='pie-graph span4' id='dc-line-chart'>
<h4>Average Rating in Stars / Number of Reviews (Line)</h4>
</div>
</div>
<!-- /other little graphs go here -->
<div class='row-fluid'>
<div class='span12 table-graph'>
<h4>Data Table for Filtered Businesses</h4>
<table class='table table-hover dc-data-table' id='dc-table-graph'>
<thead>
<tr class='header'>
<th>Name</th>
<th>City</th>
<th>Review Score (in Stars)</th>
<th>Total Reviews</th>
<th>Location</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
<div class='remaining-graphs span4'>
<div class='row-fluid'>
<div class='row-graph span12' id='dc-row-graph' style='color:black;'>
<h4>Reviews Per City</h4>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
The following call:
d3.json("data/yelp_test_set_business.json", function (yelp_data) { ...}
returns nothing in yelp_data
Does anyone know why this is hapenning?
Instead of doing like this to load your JSON:
d3.json("data/yelp_test_set_business.json", function (yelp_data) {
It should have been:
d3.json("data/yelp_test_set_business.json", function (error, yelp_data) {
Read this
Most of the time, this error is related to the fact that you just open the html file in your browser, which then tries to open the json file using the file:/// protocol, resulting in a cross origin violation.
D3 doc on requests.
One way to fix it is just by using a web server to serve the .html and the .json.
If you have python installed, just go to the folder where you file is located and run python -m SimpleHTTPServer, then navigate with your browser to http://localhost:8080. This way both the .html and .json will be served from the same origin (namely localhost:8080), and you will be able to load file via d3.json, d3.csv, etc...

Resources