I have two bar charts, one of them shows top ten categories, the other one shows min ten categories.
There is no problem with top ten bar chart, but I use the very same code to draw min ten bar chart that I used in top ten bar chart, but here is the result.
This is top ten:
And this is min ten:
As you can see above image, min ten chart's bars look like damaged and labels below the bars can't be seen.
And here is the codes that produce these results:
MinTen produced html which feeds the bar chart:
var initCatMinTenBarChart = function ()
{
var dataMin = new Array();
var ticks = [[0, 'Kozmetik'],[1, 'Ev&Yaşam'],[2, 'Cep Telefonu'],[3, 'Ayakkabı'],[4, 'Bilgisayar'],[5, 'Beyaz Eşya']];var labels = ['Kozmetik','Ev&Yaşam','Cep Telefonu','Ayakkabı','Bilgisayar','Beyaz Eşya'];var minTenCat_d1 = 18;var minTenCat_d2 = 12;var minTenCat_d3 = 8;var minTenCat_d4 = 7;var minTenCat_d5 = 6;var minTenCat_d6 = 4;
var isempty = false;
var d_min_ten_cat_bar = [[0,18],[1,12],[2,8],[3,7],[4,6],[5,4]];
dataMin.push({
label: labels,
data: d_min_ten_cat_bar,
bars: {
show: true,
barWidth: 0.2,
order: 1
}
});
if (!isempty) {
$.plot("#MinTenCatBarChart", dataMin, $.extend(true, {}, Plugins.getFlotDefaults(), {
legend: {
show: false
},
series: {
lines: { show: false },
points: { show: false }
},
grid: {
hoverable: true,
clickable: true
},
tooltip: true,
tooltipOpts: {
content: function (label, x, y) { return 'İçerik Sayısı: ' + y; }
},
bars: {
align: "center",
barWidth: 0.1
},
xaxis: {
align: "center",
ticks: ticks
},
yaxis: {
tickLength: 0
}
}));
}
}
TopTen produced html data which feeds the bar chart:
var initCatTopTenBarChart = function ()
{
var dataTop = new Array();
var ticks = [[0, 'Kozmetik'],[1, 'Ev&Yaşam'],[2, 'Cep Telefonu'],[3, 'Ayakkabı'],[4, 'Bilgisayar'],[5, 'Beyaz Eşya']];var labels = ['Kozmetik','Ev&Yaşam','Cep Telefonu','Ayakkabı','Bilgisayar','Beyaz Eşya'];var topTenCat_d1 = 18;var topTenCat_d2 = 12;var topTenCat_d3 = 8;var topTenCat_d4 = 7;var topTenCat_d5 = 6;var topTenCat_d6 = 4;
var isempty = false;
var d_top_ten_cat_bar = [[0,18],[1,12],[2,8],[3,7],[4,6],[5,4]];
dataTop.push({
label: labels,
data: d_top_ten_cat_bar,
bars: {
show: true,
barWidth: 0.2,
order: 1
}
});
if (!isempty) {
$.plot("#TopTenCatBarChart", dataTop, $.extend(true, {}, Plugins.getFlotDefaults(), {
legend: {
show: false
},
series: {
lines: { show: false },
points: { show: false }
},
grid: {
hoverable: true,
clickable: true
},
tooltip: true,
tooltipOpts: {
content: function (label, x, y) { return 'İçerik Sayısı: ' + y; }
},
bars: {
align: "center",
barWidth: 0.1
},
xaxis: {
align: "center",
ticks: ticks
},
yaxis: {
tickLength: 0
}
// tick olasyına bak 0,10 arası işlemez burda max-min şeyapmak lazım
}));
}
}
Maybe the positioning is off because of when the chart is being rendered? Try drawing the charts only once their corresponding tab is selected and visible.
Related
I recently came across this really nice example: https://jsfiddle.net/BlackLabel/7t59w4po/
Basically, what it does is that it synchronizes the drag of a line in one graph in all the other graphs.
I was wondering if someone could help me out to reproduce the same example, but instead of one vertical line, I would like to have two. Is this possible?
Thank you!
JS Code:
/*
The purpose of this demo is to demonstrate how multiple charts on the same page
can be linked through DOM and Highcharts events and API methods. It takes a
standard Highcharts config with a small variation for each data set, and a
mouse/touch event handler to bind the charts together.
*/
/**
* In order to synchronize tooltips and crosshairs, override the
* built-in events with handlers defined on the parent element.
*/
['mousemove', 'touchmove', 'touchstart'].forEach(function(eventType) {
document.getElementById('container').addEventListener(
eventType,
function(e) {
var chart,
point,
i,
event;
for (i = 0; i < Highcharts.charts.length; i = i + 1) {
chart = Highcharts.charts[i];
// Find coordinates within the chart
event = chart.pointer.normalize(e);
// Get the hovered point
point = chart.series[0].searchPoint(event, true);
if (point) {
point.highlight(e);
}
}
}
);
});
/**
* Override the reset function, we don't need to hide the tooltips and
* crosshairs.
*/
Highcharts.Pointer.prototype.reset = function() {
return undefined;
};
/**
* Highlight a point by showing tooltip, setting hover state and draw crosshair
*/
Highcharts.Point.prototype.highlight = function(event) {
event = this.series.chart.pointer.normalize(event);
this.onMouseOver(); // Show the hover marker
this.series.chart.tooltip.refresh(this); // Show the tooltip
this.series.chart.xAxis[0].drawCrosshair(event, this); // Show the crosshair
};
/**
* Synchronize zooming through the setExtremes event handler.
*/
function syncExtremes(e) {
var thisChart = this.chart;
if (e.trigger !== 'syncExtremes') { // Prevent feedback loop
Highcharts.each(Highcharts.charts, function(chart) {
if (chart !== thisChart) {
if (chart.xAxis[0].setExtremes) { // It is null while updating
chart.xAxis[0].setExtremes(
e.min,
e.max,
undefined,
false, {
trigger: 'syncExtremes'
}
);
}
}
});
}
}
/**
* Synchronize annotations drag&drop
*/
function syncAnnotations(e) {
var thisChart = this.chart;
var newX = this.options.shapes[0].points[0].x
if (e.type !== 'afterUpdate') {
Highcharts.each(Highcharts.charts, function(chart) {
if (chart !== thisChart) {
chart.annotations[0].update({
labels: [{
point: {
x: newX
}
}],
shapes: [{
points: [{
x: newX,
xAxis: 0,
y: 0
}, {
x: newX,
xAxis: 0,
y: 1000
}]
}]
});
}
});
}
}
// Get the data. The contents of the data file can be viewed at
Highcharts.ajax({
url: 'https://cdn.jsdelivr.net/gh/highcharts/highcharts#v7.0.0/samples/data/activity.json',
dataType: 'text',
success: function(activity) {
activity = JSON.parse(activity);
activity.datasets.forEach(function(dataset, i) {
// Add X values
dataset.data = Highcharts.map(dataset.data, function(val, j) {
return [activity.xData[j], val];
});
var chartDiv = document.createElement('div');
chartDiv.className = 'chart';
document.getElementById('container').appendChild(chartDiv);
Highcharts.chart(chartDiv, {
chart: {
marginLeft: 40, // Keep all charts left aligned
spacingTop: 20,
spacingBottom: 20
},
title: {
text: dataset.name,
align: 'left',
margin: 0,
x: 30
},
credits: {
enabled: false
},
legend: {
enabled: false
},
xAxis: {
crosshair: true,
events: {
setExtremes: syncExtremes
},
labels: {
format: '{value} km'
}
},
yAxis: {
title: {
text: null
}
},
annotations: [{
draggable: 'x',
animation: {
defer: false
},
events: {
drag: syncAnnotations,
afterUpdate: syncAnnotations
},
shapes: [{
strokeWidth: 3,
type: 'path',
points: [{
x: 3,
y: 0,
xAxis: 0
}, {
x: 3,
y: 1000,
xAxis: 0
}]
}],
labels: [{
point: {
x: 3,
y: 30,
xAxis: 0
},
shape: 'rect',
formatter: function(e) {
// Use shape options because value is available there. Label use translation only
return this.target.annotation.shapes[0].options.points[0].x.toFixed(3);
}
}]
}],
tooltip: {
positioner: function() {
return {
// right aligned
x: this.chart.chartWidth - this.label.width,
y: 10 // align to title
};
},
borderWidth: 0,
backgroundColor: 'none',
pointFormat: '{point.y}',
headerFormat: '',
shadow: false,
style: {
fontSize: '18px'
},
valueDecimals: dataset.valueDecimals
},
series: [{
data: dataset.data,
name: dataset.name,
type: dataset.type,
color: Highcharts.getOptions().colors[i],
fillOpacity: 0.3,
tooltip: {
valueSuffix: ' ' + dataset.unit
}
}]
});
});
}
});
You only need to add another annotation:
annotations: [{
...,
{
...
}],
And improve the syncAnnotations function a little bit:
function syncAnnotations(e) {
var thisChart = this.chart;
var newX = this.options.shapes[0].points[0].x
var index = this.chart.annotations.indexOf(this);
if (e.type !== 'afterUpdate') {
Highcharts.each(Highcharts.charts, function(chart) {
if (chart !== thisChart) {
chart.annotations[index].update({
...
});
}
});
}
}
Live demo: https://jsfiddle.net/BlackLabel/jwtLc379/
API Reference: https://api.highcharts.com/highcharts/annotations
I've seen tutorials and posts about getting data from the x axis into the tooltip but I am overriding it with categories and cannot figure out how to get the x axis to show up in the tooltip.
This is what im working with:
function showTooltip(x, y, contents) {
$('<div id="tooltip" class="flot-tooltip tooltip"><div class="tooltip-arrow"></div>' + contents + '</div>').css({
top: y - 43,
left: x - 15,
}).appendTo("body").fadeIn(200);
}
var data = [[1492854610, -1240],[1492939020, -1273],[1493025073, -1279],[1493117066, -1186],[1493198484, -1269],[1493289175, -1198],[1493370646, -1280],[1493458518, -1255],[1493543731, -1275],[1493630250, -1273],[1493716306, -1279],[1493803609, -1264],[1493889258, -1276],[1493975557, -1278],[1494064529, -1235],[1494155440, -1160],[1494237980, -1224],[1494321047, -1280],[1494407990, -1271],[1494494125, -1275],[1494581609, -1257],[1494668321, -1252],[1494753220, -1277],[1494847855, -1140],[1494925963, -1278],[1495012537, -1275],[1495099289, -1269],[1495188205, -1227],[1495273568, -1244],[1495358329, -1272]];
$.plot($("#placeholder"), [{
label: "Delay: ",
data: data,
color: "#3a8ce5"
}], {
xaxis: {
mode: "categories",
tickLength: 0,
ticks: [[0, "1:50 AM"],[1, "1:17 AM"],[2, "1:11 AM"],[3, "2:44 AM"],[4, "1:21 AM"],[5, "2:32 AM"],[6, "1:10 AM"],[7, "1:35 AM"],[8, "1:15 AM"],[9, "1:17 AM"],[10, "1:11 AM"],[11, "1:26 AM"],[12, "1:14 AM"],[13, "1:12 AM"],[14, "1:55 AM"],[15, "3:10 AM"],[16, "2:06 AM"],[17, "1:10 AM"],[18, "1:19 AM"],[19, "1:15 AM"],[20, "1:33 AM"],[21, "1:38 AM"],[22, "1:13 AM"],[23, "3:30 AM"],[24, "1:12 AM"],[25, "1:15 AM"],[26, "1:21 AM"],[27, "2:03 AM"],[28, "1:46 AM"],[29, "1:18 AM"]]
},
yaxis: {
min: -2000,
max: 1000,
},
series: {
lines: {
show: true,
fill: true
},
points: {
show: true,
}
},
grid: {
hoverable: true,
clickable: true,
markings: [
{ color: '#000', lineWidth: 1, yaxis: { from: 0, to: 0 } },
]
},
legend: {
show: false
}
});
$("#placeholder").bind("plothover", function(event, pos, item) {
if (item) {
if (previousPoint != item.dataIndex) {
previousPoint = item.dataIndex;
$("#tooltip").remove();
var y = item.datapoint[1].toFixed();
showTooltip(item.pageX, item.pageY,
item.series.label + " = " + y);
}
} else {
$("#tooltip").remove();
previousPoint = null;
}
});
I am trying to get the times part of the categories. The item array has 3 pieces of data, none of which are the times
jFiddle:
http://jsfiddle.net/zw14y8c3/2/
The item.datapoint[0] data has the index of the x-axis tick. With that you can get the actual tick label from the ticks array:
var x = $("#placeholder").data('plot').getAxes().xaxis.ticks[item.datapoint[0]].label;
See the updated fiddle for the full example.
Hi I am using the following scatter chart code
https://www.tutorialspoint.com/highcharts/highcharts_scatter_basic.htm
In this I need no negative values. when I pass only positive values data it is solved.
I need to make my y axis value reversed. That is Y axis should start with zero.
x axis same.
Kindly help me to do it. My code is as below
<html>
<head>
<title>User Interaction </title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="ourgraph.js"></script>
</head>
<body>
<div id="container" style="width: 550px; height: 400px; margin: 0 auto"></div>
<script language="JavaScript">
var edata;
var i;
//a = new Array();
$(document).ready(function() {
var chart = {
type: 'scatter',
zoomType: 'xy'
};
var title = {
text: 'User Interaction Touch points'
};
var subtitle = {
text: 'Source: charmboard database'
};
var xAxis = {
//range = [0,320]
title: {
enabled: true,
text: 'Height (px)'
},
startOnTick: true,
endOnTick: true,
showLastLabel: true
};
var yAxis = {
//range = [0,180]
title: {
text: 'Width (px)'
}
};
var legend = {
layout: 'vertical',
align: 'left',
verticalAlign: 'top',
x: 100,
y: 70,
floating: true,
backgroundColor: (Highcharts.theme && Highcharts.theme.legendBackgroundColor) || '#FFFFFF',
borderWidth: 0.1
}
var plotOptions = {
scatter: {
marker: {
radius: 0.5,
states: {
hover: {
enabled: true,
lineColor: 'rgb(100,100,100)'
}
}
},
states: {
hover: {
marker: {
enabled: false
}
}
},
tooltip: {
headerFormat: '<b>{series.name}</b><br>',
pointFormat: '{point.x} x-px, {point.y} y-px'
}
}
};
// http call for data
$.ajax({
url: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
type: 'GET',
context: document.body,
success: function(data){
//console.log(data[0]);
//console.log(data[1]);
//console.log(data);
// http call for end
//writeing a data to file starts with removed time slot colum, negatvie values , x axis 0-320 ,y axis 0-180 alone
// data.forEach(function(i)
//{
// if (i[0]> 0 && i[0] < 320 && i[1] >0 && i[1] <180)
// {
//
// edata = data.slice(2);
//}
//});
//writeing a data to file ends with removed time slot colum, negatvie values , x axis 0-320 ,y axis 0-180 alone
var series= [{
name: 'Touches',
color: 'rgba(223, 83, 83, .5)',
data: data
}
];
var json = {};
json.chart = chart;
json.title = title;
json.subtitle = subtitle;
json.legend = legend;
json.xAxis = xAxis;
json.yAxis = yAxis;
json.series = series;
json.plotOptions = plotOptions;
$('#container').highcharts(json);
}
});
});
</script>
</body>
</html>
Since the tutorial is using HighCharts it would be good idea to open there docs.
As for answer you need to change this:
var yAxis = {
title: {
text: 'Weight (kg)'
}
};
To this:
var yAxis = {
title: {
text: 'Weight (kg)'
},
min: 0 // Make sure you add this.
};
Hope that helps!
I have a graph plotted with a multitude of lines, a rather big figure overlapping the lines and ontop of that is a point that is capable of being drag and dropped all over the plot by the user.
The problem I'm facing currently is that as soon as the user drags and drops the point straight ontop of a line or a point of the figure, the user is unable to drag and drop the point away. I have set up a fiddle with my current setup.
JavaScript/jQuery code:
$(function() {
var startPoint = [[7.00, 0]];
var line10 = HHIsoPleth(7.00, 7.80, 10);
var line120 = HHIsoPleth(7.00, 7.80, 120);
var options = {
series: {
points: {
editMode: "none",
show: true,
radius: 0,
symbol: "circle",
fill: true,
hoverable: false,
},
lines: {
editMode: "none",
editable: false,
hoverable: false,
clickable: false
}
},
yaxes: [ {
position: "left",
min: 0, max: 60,
tickSize: 4,
} ],
xaxes: [ {
position: "bottom",
min: 7.00, max: 7.80,
} ],
grid: {
backgroundColor: "transparent",
editable: true,
hoverable: true,
clickable: false,
},
legend: {
position: "nw"
},
};
var data = [
{ data: line10, label: "PCO2", lines: { show: true, lineWidth: 1 }, points: { show: false }, editable: false, clickable: false, hoverable: false, color: "#FF0000" },
{ data: line120, lines: { show: true, lineWidth: 1 }, points: { show: false }, editable: false, clickable: false, hoverable: false, color: "#FF0000" },
{ data: startPoint, label: "Bloedzuur gehalte", lines: { show: true }, points: { show: true, radius: 3 }, editable: true, editMode: 'xy', color: '#00FF00' },
];
var plot = $.plot($("#flot-placeholder"), data, options);
// Drag and drop
$("#flot-placeholder").bind("datadrop", function(event, pos, item) {
var PCO2 = getPCO2(pos.x1.toFixed(2), pos.y1.toFixed(2));
var pH = getPH(pos.y1.toFixed(2), PCO2);
var HCOmm = getHCO3(pH, PCO2);
updatePoint(pH, HCOmm);
});
// Generate red lines / isopleths
function HHIsoPleth(minPH, maxPH, PCO2){
var isoPleth = [];
for (var i = minPH; i < maxPH; i+=0.01){
HCOmm = (0.03 * PCO2 * Math.pow(10,i-6.1));
isoPleth.push([i,HCOmm]);
}
return isoPleth;
}
function getHCO3(ph, pco2) {
return 0.03 * pco2 * Math.pow(10, ph - 6.1);
}
function getPH(hco3, pco2) {
return 6.1 + Math.log10(hco3 / (0.03 * pco2));
}
function getPCO2(ph, hco3) {
return (hco3 / 0.03) * Math.pow(10, 6.1 - ph);
}
//Reset point
$("#davenportReset").click(function() {
updatePoint(7.00, 0);
});
function updatePoint(x, y) {
data[16].data[0] = [x, y];
$.plot($("#flot-placeholder"), data, options);
}
// Debug purpose, get the index of the point that is clicked
$("#placeholder").bind("plotdown", function(event,pos,item){
$("#log").append("\nplotdown(" + item.seriesIndex + ")");
});
});
Additional libraries: Flot.js, JUMFlot
HTML:
<input class="davenportInput" id="davenportReset" type="button" value="Reset Point" />
<div id="flot-placeholder" style="width:558px;height:511px"></div>
eventlog<textarea id="log" rows="15" cols="28"></textarea>
In the provided fiddle you'll see that you can drag and drop the green point all around the plot. But once you drop it ontop any of the red lines it is no longer possible to drag and drop the green point somewhere else. In the textarea you'll see that when you click the green point, plotdown(16) will be shown in the textarea. But will show plotdown(0-15) when it is clicked when the point is over any of the red/yellow lines.
Would it be possible to get the 16th data serie(the drag and drop point) when it's overlapping any of the red lines?
Using (once again) Mark's answer I solved it. One condition I had though was that I had to keep the green point above all other lines.
This is what I did:
var startPoint = [[7.00, 0]];
var invisPoint = [[7.00, 0]];
var line10 = HHIsoPleth(7.00, 7.80, 10);
var line120 = HHIsoPleth(7.00, 7.80, 120);
To create a invisible placeholder point.
I than added it to the data object
var data = [
{ data: invisPoint , lines: { show: false }, points: { show: false, radius: 3 }, editable: true, editMode: 'xy', color: '#00FF00' },
{ data: line10, label: "PCO2", lines: { show: true, lineWidth: 1 }, points: { show: false }, editable: false, clickable: false, hoverable: false, color: "#FF0000" },
{ data: line120, lines: { show: true, lineWidth: 1 }, points: { show: false }, editable: false, clickable: false, hoverable: false, color: "#FF0000" },
{ data: startPoint, label: "Bloedzuur gehalte", lines: { show: true }, points: { show: true, radius: 3 }, editable: true, editMode: 'xy', color: '#00FF00' },
];
And updated the updatePoint function
function updatePoint(x, y) {
var data = plot.getData();
data[0].data[0] = [x, y]; // Invisible point
data[17].data[0] = [x, y]; // Green point
plot.setData(data);
plot.draw();
}
This way, the invisible point gets selected and dragged and dropped. I simply use those coordinates to position the green point aswell.
Internally, flot or jumflot in this case, when you mousedown is searching the points to see if one is near enough to your mouse cursor. It searches the points in order and finds your line segment before the point. So, simple fix, place your move-able point first:
var data = [
{ data: startPoint, label: "Bloedzuur gehalte", lines: { show: true }, points: { show: true, radius: 3 }, editable: true, editMode: 'xy', color: '#00FF00' },
{ data: line10, label: "PCO2", lines: { show: true, lineWidth: 1 }, points: { show: false }, editable: false, clickable: false, hoverable: false, color: "#FF0000" },
....
In addition, update your plot like this:
function updatePoint(x, y) {
var data = plot.getData();
data[0].data[0] = [x, y];
plot.setData(data);
plot.draw();
}
Calling $.plot over and over again is expensive and will probably leak memory (it used to at least - not sure if it was every fixed).
Updated fiddle.
I want to process data from a .csv file to:
Divide the data coming in by 10, e.g., 588 => 58.8
Remove outliers from the data or to change to zero, e.g., 8888 => 0
Here is my javascript, I appreciate the help!!
$.get('http://www.geoinc.org/Dropbox/geo/sites/GC_ROOM/charts/hassayampa.csv', function(data)
{
// Split the lines
var lines = data.split('\n');
var i = 0;
var csvData = [];
// Iterate over the lines and add categories or series
$.each(lines, function(lineNo, line)
{
csvData[i] = line.split(',');
i = i + 1;
});
var columns = csvData[0];
var categories = [], series = [];
for(var colIndex=0,len=columns.length; colIndex<len; colIndex++)
{
//first row data as series's name
var seriesItem=
{
data:[],
name:csvData[0][colIndex]
};
for(var rowIndex=1,rowCnt=csvData.length; rowIndex<rowCnt; rowIndex++)
{
//first column data as categories,
if (colIndex == 0)
{
categories.push(csvData[rowIndex][0]);
}
else if(parseFloat(csvData[rowIndex][colIndex])) // <-- here
{
seriesItem.data.push(parseFloat(csvData[rowIndex][colIndex]));
}
};
//except first column
if(colIndex>0)series.push(seriesItem);
}
// Create the chart
var chart = new Highcharts.Chart(
{
chart:
{
renderTo: 'test',
type: 'line',
zoomType: 'x',
},
title: {
text: 'Daily Average Temperature',
x: -20 //center
},
subtitle: {
text: 'Source: HASSAYAMPA',
x: -20
},
xAxis:
{
categories: categories,
labels:
{
step: 80,
},
tickWidth: 0
},
yAxis:
{
title: {
text: 'Temperature (\xB0C)'
},
//min: 0
},
tooltip:
{
formatter: function()
{
return '<b>'+ this.series.name +'</b><br/>'+ this.x +': '+ this.y +'\xB0C';
}
},
legend:
{
layout: 'vertical',
//backgroundColor: '#FFFFFF',
//floating: true,
align: 'left',
//x: 100,
verticalAlign: 'top',
//y: 70,
borderWidth: 0
},
plotOptions:
{
area:
{
animation: false,
stacking: 'normal',
lineColor: '#666666',
lineWidth: 1,
marker:
{
lineWidth: 1,
lineColor: '#666666'
}
}
},
series: series
});
});
I'm not sure what you are asking, but I'll take a shot at it...
First things first, this snippet of code is not sound. It'll not only skip NaNs but 0s as well (which is valid numeric data):
else if(parseFloat(csvData[rowIndex][colIndex]))
{
seriesItem.data.push(parseFloat(csvData[rowIndex][colIndex]));
}
Instead I'd do:
//first column data as categories,
if (colIndex == 0)
{
categories.push(csvData[rowIndex][0]);
}
else
{
var fVal = parseFloat(csvData[rowIndex][colIndex]);
if (!isNaN(fVal))
{
fVal = fVal / 10.0; //<-- here's the division!!
seriesItem.data.push(fVal);
}
}
As far as how to exclude outliers, the big question there is how do you want to exclude outliers? A simple min/max criteria? Then just check that fVal is within those limits before seriesItem.data.push...