Can anyone suggest analog of spinner from jQuery UI, that has ability to display strings instead of specific values? In my example I have two widgets, spinner and select, where select shows string "Foobar" when values is -1, but I want to use spinner to not to bound values to ones from list. http://jsfiddle.net/andrewboltachev/qk6bn/
I want actual plugin to have options like these:
$('#myspinner').spinner({
'min': -1,
'values_to_text': function(x) {
if (x === -1) return 'FooBar';
return x;
}
});
Related
I am using amcharts 4 to display temperature lines. At times there are many stations so I would like to have just one tooltip and just for the value the cursor is at instead of one tooltip for every line (because then they overlap and some are unreadable).
And there might be several stations with the same temperature so I would have to list all of them in the tooltip.
Anyone knows how to achieve that?
In amcharts 3 I used a balloonFunction attached to the graphs to create my own tooltip. But yet I couldn't find how to do it with the series in amcharts 4.
Thanks for a hint!
So as David Liang mentioned, since all the data items converge along their x axis value (a datetime in this case), you can limit tooltips down to one by only setting one series' tooltipText, and it will have access to the rest of the data fields via data placeholders. E.g. even though series1's value field is E852_t4m, it can use series30's value by just putting "{median_tBel}".
But if you want to have a tooltip based on which line you're hovering over, how to do that depends whether or not you require the Chart Cursor.
If you don't need it, simply set the tooltipText on the line's bullets, e.g.
series1.bullets.getIndex(0).tooltipText = "{name} {valueY}°C";
Here's a demo of your fiddle with that:
https://codepen.io/team/amcharts/pen/803515896cf9df42310ecb7d8d7a2fb7
But if you require Chart Cursor, unfortunately there isn't a supported option at the moment. There's a kind of workaround but it's not the best experience. You start with doing the above. The Chart Cursor will trigger hover effects on all lines and their bullets, including triggering their tooltips. A bullet's tooltip is actually its series' (series1.bulletsContainer.children.getIndex(0).tooltip === series1.tooltip). If we remove the reference to the bullet's tooltip, e.g. series1.bullets.getIndex(0).tooltip = undefined;, the chart will check up the chain and refer to series' anyway. If we do the same to the series' tooltip, it'll go up the chain to chart.tooltip, if we do this to all series, we basically turn chart.tooltip into a singleton behavior of sorts. But it's not as responsive to mouseovers.
You'll see what I mean with this demo:
https://codepen.io/team/amcharts/pen/244ced223fe647ad6df889836da695a8
Oh, also in the above, you'll have to adjust the chart's tooltip to appear on the left/right of bullets with this:
chart.tooltip.pointerOrientation = "horizontal";
Edit:
Since the first method sufficed, I've updated it with an adapter that checks for other fields in range. In the adapter, the target will be the CircleBullet, target.dataItem.valueY is the currently hovered value, and target.dataItem.dataContext are the other fields at the same date.
This is how I modified tooltipText to show other series within +/-0.5C range of the currently-hovered bullet:
// Provide a range of values for determining what you'll consider to be an "overlap"
// (instead of checking neighboring x/y coords.)
function inRange(valueA, rangeA, rangeB) {
return valueA >= rangeA && valueA <= rangeB;
}
// Provide adapters for tooltipText so we can modify them on the fly
chart.series.each(function(series) {
series.bullets
.getIndex(0)
.adapter.add("tooltipText", function(tooltipText, target) {
// the other data fields will already match on the date/x axis, so skip
// the date and this bullet's data fields.
// (target.dataItem.component is the target's series.)
var skipFields = ["date", target.dataItem.component.dataFields.valueY];
// this bullet's value
var hoveredValue = target.dataItem.valueY;
// all the other data fields at this date
var data = target.dataItem.dataContext;
// flag for adding additional text before listing other nearby bullet values
var otherPoints = false;
Object.keys(target.dataItem.dataContext).forEach(function(field) {
// if the field is neither date, nor bullet's
if (!~skipFields.indexOf(field)) {
if (inRange(data[field], hoveredValue - 0.5, hoveredValue + 0.5)) {
if (!otherPoints) {
tooltipText += "\n\nOthers:";
otherPoints = true;
}
// Keep {data placeholder} notation to retain chart formatting features
tooltipText += "\n" + field + ": {" + field + "}°C";
}
}
});
return tooltipText;
});
});
If your series' data points have different x values, it's impossible to combine all the information into one tooltip.
But if they do have same x values, you can just turn on the tooltip for just one of the series:
...,
series: [{
type: "LineSeries",
tooltipHTML: `xxx`,
...
}, {
type: "LineSeries",
...
}, {
type: "LineSeries",
...
}],
...
And within the tooltip HTML, you have access to the data:
...,
tooltipHTML: `
<strong>Year: </strong>{year}<br />
<strong>Cars: </strong>{cars}<br />
<strong>Motorcycles: </strong>{motorcycles}<br />
<strong>Bicycles: </strong>{bicycles}
`,
...
demo: http://jsfiddle.net/davidliang2008/aq9Laaew/286519/
I'm using the angularjs ui-grid and I have a "total" row that I want to pin to the top of the grid no matter what is the current sorting.
How can I accomplish that?
I think this is what you are looking for : Row Pinning
Essentially add another hidden column, something like this:
{
field: 'pinned',
visible: false,
sort: {direction: uiGridConstants.ASC, priority: 0}, //use uiGridConstants.DESC for pinning to the bottom
suppressRemoveSort: true,
sortDirectionCycle: [uiGridConstants.ASC] //use uiGridConstants.DESC for pinning to the bottom
}
Row entities which have pinned = true rise to the top, even when other sorting are applied.
DISCLAIMER: I know it's not exactly answers the question, but this is how I solved it for now until I'll have a better solution:
Create an other grid above the main grid :
<div style="height:30px" ui-grid="totalGridOptions"></div>
<div ui-grid="gridOptions" class="grid"></div>
with definitions:
$scope.totalGridOptions = {showHeader:false,enableHorizontalScrollbar:false};
and then bind the columns of the main grid to the total grid (for width and other adjustments):
$scope.$watch('gridOptions', function (newVal) {
$scope.totalGridOptions.columnDefs = newVal.columnDefs;
}, true);
I think you should use something like this
$scope.gridOptions.data.unshift({label:value});
unshift adds it to the top
Edit 2 / Actual Solution: The way I finally settled this issue was by using custom header cell templates. I essentially create a second header row by adding a div at the bottom of what was previously my header. Here's a simple version:
<div class="super-special-header>
<div class="header-display-name">{{col.displayName}}</div>
<div class="totals-row">{{grid.appScope.totals[col.field]}}</div>
</div>
I store my totals on the controller's $scope and can access them in that div with grid.appScope.totals[whateverThisColumnIs]. This way I can still update them dynamically, but they don't get mixed into a sort function like my previous 'solution' was aiming for.
Edit 1 / Dead-end 'solution': Just ran into a problem with my solution, if your table is long (you have to scroll to get to bottom rows), the totals row will scroll out of view. Going to leave this 'solution' here so no one else makes the same mistake!
I had this same issue but with a twist. Since I was going to need to change the default sorting algorithms for many of the columns anyway, I set my algorithm up to skip the first element in the sort. You can use the sortingAlgorithm property on any columndef that would be part of a sortable column. This is really only a solution if you have only a few sortable columns though. It becomes unmaintainable for huge tables.
I couldn't find any built-in feature for ui-grid to keep a specific row at the top of the grid when sorting is applied. But this could be done using sortingAlgorithm parameter in the columnDefs( please refer to http://ui-grid.info/docs/#!/tutorial/Tutorial:%20102%20Sorting).
I have written an algorithm which keeps the row('total' is the particular cell value in the row) at the top of the grid without applying a sorting.
var sortingAlgorithm = function (a, b, rowA, rowB, direction) {
if (direction == 'total') {
if (a == 'total') {
return 0;
}
return (a < b) ? -1 : 1;
} else {
if (a == 'total') {
return 0;
}
if (b == 'total') {
return 1;
}
}
}
I am attempting to create a bar graph that when independent sliders are moved they change two bar graph svg heights at the same time and they are stacked, they are different colors show it shows two separate values in the same graph, basically showing growth vs the current. I am using jquery-ui and D3.js. Currently it only moves the one svg elements instead of both at the same time, Id like them both to move at the same time.
HTML
<div id="slider" class="slider">
<label for="amount">Age</label>
<input type="text" id="amount1" style="border:0; font-weight:bold;">
</div>
<div id="slider1" class="slider">
<label for="amount2">Retirement Age</label>
<input type="text" id="amount2" style="border:0; font-weight:bold;">
</div>
JS
//initialize sliders
jQuery(document).ready(function($) {
$("#slider").slider({
max: 100
});
$("#slider").slider({
min: 18
});
$("#slider1").slider({
max: 100
});
$("#slider1").slider({
min: 18
});
//slider actions
$("#slider, #slider1").slider({
value: 10,
animate: "fast" ,
slide: function (event, ui) {
//capture the value of the specified slider
var selection = $("#slider").slider("value");
var selection1 = $("#slider1").slider("value");
//fill the input box with the slider value
$( "#amount1" ).val( selection );
$( "#amount2" ).val( selection1 );
//set width and height, actually I'm a little confused what this is for
var w = 200;
var h = 200;
//data arrays for svgs
var dataset = [];
var dataset1 = [];
//fill the data arrays with slider values
dataset.push(selection);
dataset.push(selection1 + selection);
//draw rectangle on the page
var rectangle = svg.selectAll("rect")
.classed("collapse", true)
.data(dataset);
**
THIS IS WHERE IT CONFUSES ME
**
//I draw the second rectangle here, however I choose the same svg element,
//Im not sure what other way to get it to appear in the same space but
//I am sure this is what is causing my issues
var rectangle1 = svg.selectAll("rect")
.classed("collapse", true)
.data(dataset1);
//not sure what this does
rectangle.enter().append("rect");
rectangle1.enter().append("rect");
rectangle.attr("width", 200).transition().attr("fill", "#A02222").attr("height", function (d) { console.log('d is ' + d);
return d;
}).attr("x", function (d) {
return 40; //I dont know why I return 40?
}).attr("y", function (d) {
return 40; //Same here dont know why I return 40?
});
rectangle1.attr("width", 200).transition().attr("height", function (d) { console.log('d is ' + d);
return d;
}).attr("x", function (d) {
return 40; //I dont know why I return 40?
}).attr("y", function (d) {
return 40; //Same here dont know why I return 40?
});
}
// slider actions ends here
});
//Create SVG element
var svg = d3.select(".svgContain").append("svg").attr("width", 125).attr("height", 300);
});
For starters, you may want to follow this tutorial: http://bl.ocks.org/mbostock/3886208
The "return 40;" that you are wondering about are actually what will specify the position and dimensions of the rect's you're appending to the svg. Those shouldn't just be 40, they should be bound to values in the data set, or based on the index of the bar's series in the set of series or something more meaningful than 40.
There is a stacked bar chart data processor that will take a set of series and spit out a new set of series coordinate definitions that make it easier to calculate how rect's will stack in svg coordinate space: https://github.com/mbostock/d3/wiki/Stack-Layout
Then, there's the more general issue of how to deal with these "nested" data sets where you have series, and in the series there are values and you don't want to have to manually track and select individual series. There are several ways to handle this sort of situation. If you know you will only ever have two series, and you really want fine-grained control over each independently, you could assign the top level object an id and then start the data join for each of the plots by selecting that top level object by id... eg:
var container1 = d3.select("#myContainer1);
container1.selectAll("rect").data(myData1).append("rect");
var container2 = d3.select("#myContainer2);
container2.selectAll("rect").data(myData2).append("rect");
If you do something like that, the first select basically sets the context of the subsequent selects. So, only the rects inside of the "#myContainer1" or "#myContainer2" will get selected by each "selectAll" based on which context you're in.
The other approach is to use nested selections. Nested selections are a little more complicated to wrap your head around, but 90% of the time, this is the approach I use. With nested selections, you would restructure your data slightly and then apply nested selects/joins to bind each series to a dom element and then the values of each series to subelements of each of the series dom elements.
First, read this: http://bost.ocks.org/mike/nest/
and then try making your data something more like this:
data = [
{ key: "series1", values: [...]},
{ key: "series2", values: [...]}
];
Then, you will want to do a nested selection where you start with a selection of the "data" array and bind it to whatever svg or html element you have that wraps each of the two series.
var series = d3.select("svg").selectAll("g.series")
.data(data, function(d){return d.key; });
series.enter().append("g").attr("class", "series");
At this point, d3 will have added a "g" element to your svg element for each series and bound the series object (including the key and values array) to the appended elements. Next, you can make a nested selection to add series-specific elements to the g element... ie:
var rect = series.selectAll("rect").data(function(d) { return d.values });
rect.enter().append("rect");
Note that we used a function in our ".data(...)" call. That's because the values we want passed to the join actually depend on which specific series is being processed by D3.
Now, you'd have a rect added to the g element for each value in each series. Since you used d3 to do the data binding and you used the key function in the first select (".data(data, function(d){return d.key;}"), future selects done in the same nested/keyed manner will update the right g and rect elements.
Here's a Fiddle that demonstrates the concept:
http://jsfiddle.net/reblace/bWp8L/2/
A key takeaway is that you can update the data (including adding additional series) and the whole thing will redraw correctly according to the new nested join.
I am using the plugin the ColumnFilter to render filters for each column in Datatables.
ColumnFilter renders two input boxes (From & To) in order to perform 'range' filtering of a table.
I am trying to replace these input boxes with a jqueryUI slider but cannot seem to make it work.
I have managed to make a slider control two separate 'from' & 'too' inputs with the following code:
//SLIDER
$(function() {
var $slider = $('#Slider'),
$lower = $('input#Min'),
$upper = $('input#Max'),
min_rent = 0,
max_rent = 400;
$lower.val(min_rent);
$upper.val(max_rent);
$('#Slider').slider({
orientation: 'horizontal',
range: true,
animate: 200,
min: 0,
max: 400,
step: 1,
value: 0,
values: [min_rent, max_rent],
slide: function(event,ui) {
$lower.val(ui.values[0]);
$upper.val(ui.values[1]);
}
});
$lower.change(function () {
var low = $lower.val(),
high = $upper.val();
low = Math.min(low, high);
$lower.val(low);
$slider.slider('values', 0, low);
});
$upper.change(function () {
var low = $lower.val(),
high = $upper.val();
high = Math.max(low, high);
$upper.val(high);
$slider.slider('values', 1, high);
});
});
This works fine and I can see the values change in the two input boxes change as I move the slider.
However, when I swap the input#Min and inut#Max element for the the two input boxes that are rendered by the ColumnFilter plugin. Nothing seems to happen. I cannot see any values update in the input boxes and the table does not auto sort as expected.
Perhaps I am approaching this the wrong way? Is there any other way to make a slider work with Datatables and the Columnfilter plugin?
Many thanks!
The two input boxes used for filtering are being 'listened to' for change event but updating the values via UI sliders does not trigger this.
I had a similar problem and ended up just forcing change() on sliderstop (event triggered by slider on mouseup) because of the dynamic content being loaded on change which I didn't want changing on slide, but you could force change() on slide too.
try:
$lower.change();
$upper.change();
after updating the values with val();
Should work :)
I won't to give a different hover colors to my jquery dialog buttons. For example when the user will hover over the "Ok" button the hover color will be blue and when he hovers over the cancel button the hover color will be gray.
Can anyone tell me how can I do it?
$(document).ready(function() {
$(":button").hover(function() {
$(this).css('background', 'url(' / images / buttons / green_26.png ')');
});
$(":button").mouseout(function() {
$(this).css('background', 'url(' / images / buttons / grey_26.png ')');
});
});
Basic theory: use one or more css classes which you add to your html object dynamically on mouse-in, and remove on mouse-out. You can take a look at the jQuery hover event and some examples of how to work with attributes in jQuery to get an idea of how to do it.
In detail: There are two different ways to approach this that I can immediately think of, depending on where you want to make the ok/cancel button "decision".
Add two different classes to your stylesheet with different background colors, and add one class to each element. That means you'll need two very similar jQuery methods, but most of it can be factored out to avoid duplication.
Hard-code different class names on your buttons (or use button id's or someting) and make two different css selectors, for example something like .ok .hover { your style here } and .cancel .hover { your style here }. Then you just need one jQuery call, that hits both buttons with the jQuery selector and adds/removes the hover class.
You can use this function:
function changeButtonClass(buttonIndex, classes) {
var selector = 'div[class^=ui-dialog-buttonpane] > button';
var buttonConcerned;
if (buttonIndex >= 0) {
buttonIndex++;
buttonConcerned = $(selector + ':nth-child(' + buttonIndex + ')');
} else {
return;
}
buttonConcerned.removeClass();
buttonConcerned.addClass(classes[0]);
buttonConcerned.
hover(
function() {
$(this)
.removeClass()
.addClass(
classes[1])
},
function() {
$(this)
.removeClass()
.addClass(
classes[0])
})
.focus(
function() {
$(this)
.removeClass()
.addClass(
classes[2])
})
.blur(
function() {
$(this)
.removeClass()
.addClass(
classes[0])
});
}
And then call your function with this (for a 3 buttons dialog):
var classes = new Array('myClass', 'myClass2', 'myClass2');
changeButtonClass(0, classes);
var classes = new Array('myClass3', 'myClass4', 'myClass4');
changeButtonClass(1, classes);
changeButtonClass(2, classes);
And so it works ;)