jQuery UI SelectMenu Dropdown - opens UP instead of DOWN - jquery-ui

I'm using jQuery UI Select Menu and occasionally have some issues.
This is located at the top left of my users' pages. Using the "dropdown/address" type, sometimes (seemingly random), the dropdown opens UP instead of DOWN, and you can't select any options in it except the first one because it's all "above" the screen.
Anyone know of a setting/option in there to force it to open down? Thanks!
update.... 11/23/11 1:11pm est
Here is some code of the call:
$(function(){
$('select#selectionA').selectmenu({
style:'dropdown',
menuWidth: 220,
positionOptions: {
collision: 'none'
},
format: addressFormatting
});
});

The plugin uses the Position utility of the jQuery UI library. If you look at the default options in the source of the plugin, there is a positionOptions property that is used in the function _refreshPosition():
options: {
transferClasses: true,
typeAhead: 1000,
style: 'dropdown',
positionOptions: {
my: "left top",
at: "left bottom",
offset: null
},
width: null,
menuWidth: null,
handleWidth: 26,
maxHeight: null,
icons: null,
format: null,
bgImage: function() {},
wrapperElement: "<div />"
}
_refreshPosition: function() {
var o = this.options;
// if its a pop-up we need to calculate the position of the selected li
if ( o.style == "popup" && !o.positionOptions.offset ) {
var selected = this._selectedOptionLi();
var _offset = "0 " + ( this.list.offset().top - selected.offset().top - ( this.newelement.outerHeight() + selected.outerHeight() ) / 2);
}
// update zIndex if jQuery UI is able to process
this.listWrap
.zIndex( this.element.zIndex() + 1 )
.position({
// set options for position plugin
of: o.positionOptions.of || this.newelement,
my: o.positionOptions.my,
at: o.positionOptions.at,
offset: o.positionOptions.offset || _offset,
collision: o.positionOptions.collision || 'flip'
});
}
You can see it uses a default value 'flip' if none is provided for the collision option of the position utility which is. According to jQuery UI documentation:
flip: to the opposite side and the collision detection is run again to see if it will fit. If it won't fit in either position, the center option should be used as a fall back.
fit: so the element keeps in the desired direction, but is re-positioned so it fits.
none: not do collision detection.
So i guess you could pass an option when initializing the plugin to define none for the collision option:
$('select').selectmenu({
positionOptions: {
collision: 'none'
}
});
Have not tested yet, this is just by looking at the code.
Edit following comment
I've noticed that the version of the javascript available on github and the one used on the plugin website are not the same. I don't know which one you are using but the one used on the website does not have a positionOptions option actually, so it has no effect to specify it when calling selectmenu().
It seems it's not possible to link directly to the javascript on the site so here's some code to illustrate:
defaults: {
transferClasses: true,
style: 'popup',
width: null,
menuWidth: null,
handleWidth: 26,
maxHeight: null,
icons: null,
format: null
}
_refreshPosition: function(){
//set left value
this.list.css('left', this.newelement.offset().left);
//set top value
var menuTop = this.newelement.offset().top;
var scrolledAmt = this.list[0].scrollTop;
this.list.find('li:lt('+this._selectedIndex()+')').each(function(){
scrolledAmt -= $(this).outerHeight();
});
if(this.newelement.is('.'+this.widgetBaseClass+'-popup')){
menuTop+=scrolledAmt;
this.list.css('top', menuTop);
}
else {
menuTop += this.newelement.height();
this.list.css('top', menuTop);
}
}
I was able to make it work as I first described with the version from github. In my opinion it is a more recent/complete version. Besides it was updated a few days ago.
I have created a small test page with two selects. I've changed for both the position for the dropdown to show above.
The first one does not specify the collision option, thus 'flip' is used and the dropdown displays below because there is not enough space above.
The second has 'none' specified and the dropdown shows above even if there is not enough space.
I've put the small test project on my dropbox.

I'm the maintainer of the Selectmenu plugin. There are currently three versions, please see the wiki for more information: https://github.com/fnagel/jquery-ui/wiki/Selectmenu
I assume you're using my fork. The collision problem could be related to this fix https://github.com/fnagel/jquery-ui/pull/255, please try the latest version.
To force a scrollbar use option maxHeight.

Related

JQueryUI - droppable.touch change

I am currently trying to make an extended droppable area but I got some problem. Here is the code (explanation below) :
$('div#1').find('div[cid="draggable"]').each(function(i, e) {
$(e).after("<div id='drp' style='width:100%;'></div>");
$('div#drp').droppable({
drop:function(event, ui) {
ajaxCall = false;
ui.draggable.css("left", "0");
ui.draggable.css("top", "0");
$(event.target).after(ui.draggable);
$(this).css("height", "0px");
$(this).removeClass("isDroppable");
$(this).removeClass("mb-1");
var t = ui.draggable.attr("i");
// callPage(81758758, 'POST', {t:t,s:0}, '');
},
over:function(event, ui) {
$(this).css("height", (parseInt(ui.helper.css("height")) + 50)+"px");
$(this).addClass("isDroppable");
$(this).addClass("mb-1");
if($(this).prev().attr("o") < ui.helper.attr("o")) {
// Move to top : Un padding s'ajoute, étrangement.
}
},
out:function(event, ui) {
$(this).css("height", "0px");
$(this).removeClass("isDroppable");
$(this).removeClass("mb-1");
},
accept:function(d) {
if(d.attr("cid") == "draggable" && d.attr("i") != $(this).prev().attr("i")) {
return true;
}
},
tolerance:'touch'
});
});
So actually, I am creating, for each child of div#0, a div#drp. It means that if I have, in div#0, 5 div#card, I will have, for each div#card, a div#drp (width:100%, height:0), for a total amount of 5 div#drp. As you should have understand, each div#drp is a DROPPABLE AREA (and each div#card can be dropped on each div#drp).
But here is the problem : The only way to get this working is to set the tolerance, as it is on my code, to "touch", as long as "pointer", "intersect" & "fit" will never work because of div#drp is set to height=0px.
Now, imagine the following structure :
<div id="card" i="1" style="width:100%;height:300px;">CARDTEST</div>
<div id="grp" i="1" style="width:100%;height:0;"></div>
<div id="card" i="2" style="width:100%;height:100px;">CARDTEST</div>
<div id="grp" i="2" style="width:100%;height:0;"></div>
<div id="card" i="3" style="width:100%;height:100px;">CARDTEST</div>
<div id="grp" i="3" style="width:100%;height:0;"></div>
Here, if I move the 2nd or 3rd card, everything will be alright. But if I move the first card (with height:300px) :
First, the id#grp height will be set to "(parseInt(ui.helper.css("height")) + 50)+"px"", it means 350px.
Second, even with that, my first card will trigger the first div#drp and the second div#drp as long as it height is too big. It's like if changing the height of my div#drp doesn't extend my DROPPABLE AREA.
So, my question is : Is there a way to "actualise" the DROPPABLE AREA within the "over" event ? I tried to change the droppable event within the "over" event, I also tried to change the tolerance within the "over" event, but nothing seems to work.
Any idea ?
Solution :
There isn't anything in Droppable() making this possible. However, it is possible to update DROPPABLE AREA positions within Draggable options. See below :
$( ".selector" ).draggable({
refreshPositions: true
});
I didn't expect a DROPPABLE property to be editable through DRAGGABLE elements. Note that, according to the documentation :
"If set to true, all droppable positions are calculated on every mousemove. Caution: This solves issues on highly dynamic pages, but dramatically decreases performance."
Tested on multiples devices, it doesn't cause lags or freez, so it should be fine.
Note that this config value will also fix margins caused by any Element added on Droppable.over if the Draggable Element is under the Droppable element (In example, if you want to do the same as what Sortable() do, but with Draggable() & Droppable()).
Problem solved !

Trigger resizable in jasmine [duplicate]

I have a div element which is made jquery Resizable. It has alsoResize option set, so other elements resize simultaneously.
What I want to do, is to set size of this Resizable div element programmatically in such way, that all Resizable logic is triggered (especially this alsoResize option is taken into account).
How can I achieve that?
Update: It looks like the internals of jQuery UI have changed dramatically since I answered this and firing the event no longer works.
There's no direct way to fire the event anymore because the resizable plugin has been fundamentally changed. It resizes as the mouse is dragged rather than syncing items up at the end. This happens by it listening for the internal resize propagation event for resizable plugins which is now fired by the _mouseDrag handler. But it depends on variables set along the way, so just firing that even internally won't help.
This means even overriding it is messy at best. I'd recommend just manually resizing the alsoResize elements directly, independent of the UI widget altogether if that's possible.
But for fun let's say it isn't. The problem is that the internals of the plugin set various properties relating to previous and current mouse position in order to know how much to resize by. We can abuse use that to add a method to the widget, like this:
$.widget("ui.resizable", $.ui.resizable, {
resizeTo: function(newSize) {
var start = new $.Event("mousedown", { pageX: 0, pageY: 0 });
this._mouseStart(start);
this.axis = 'se';
var end = new $.Event("mouseup", {
pageX: newSize.width - this.originalSize.width,
pageY: newSize.height - this.originalSize.height
});
this._mouseDrag(end);
this._mouseStop(end);
}
});
This is just creating the mouse events that the resizable widget is looking for and firing those. If you wanted to do something like resizeBy it'd be an even simpler end since all we care about is the delta:
var end = $.Event("mouseup", { pageX: newSize.width, pageY: newSize.height });
You'd call the $.widget() method after jQuery UI and before creating your .resizable() instances and they'll all have a resizeTo method. That part doesn't change, it's just:
$(".selector").resizable({ alsoResize: ".other-selector" });
Then to resize, you'd call that new resizeTo method like this:
$(".selector").resizable("resizeTo", { height: 100, width: 200 });
This would act as if you instantly dragged it to that size. There are of course a few gotchas here:
The "se" axis is assuming you want resize by the bottom right - I picked this because it's by far the most common scenario, but you could just make it a parameter.
We're hooking into the internal events a bit, but I'm intentionally using as few internal implementation details as possible, so that this is less likely to break in the future.
It could absolutely break in future versions of jQuery UI, I've only tried to minimize the chances of that.
You can play with it in action with a fiddle here and the resizeBy version here.
Original answer:
You can do this:
$(".selector").trigger("resize");
alsoResize internally rigs up a handler to the resize event, so you just need to invoke that :)
You can trigger the bars programmatically. For example, to trigger the east-west resize event:
var elem =... // Your ui-resizable element
var eastbar = elem.find(".ui-resizable-handle.ui-resizable-e").first();
var pageX = eastbar.offset().left;
var pageY = eastbar.offset().top;
(eastbar.trigger("mouseover")
.trigger({ type: "mousedown", which: 1, pageX: pageX, pageY: pageY })
.trigger({ type: "mousemove", which: 1, pageX: pageX - 1, pageY: pageY })
.trigger({ type: "mousemove", which: 1, pageX: pageX, pageY: pageY })
.trigger({ type: "mouseup", which: 1, pageX: pageX, pageY: pageY }));
I am doing a 1px left followed by 1px right movement on the east bar handle.
To perform a full size, you can target .ui-resizable-handle.ui-resizable-se if you have east and south resize bars.
I needed the same thing for tests. Similar questions have only one promising answer https://stackoverflow.com/a/17099382/1235394, but it requires additional setup, so I ended with my own solution.
I have an element with resizable right edge
$nameHeader.resizable({handles: 'e', ... });
and I needed to trigger all callbacks during the test in order to resize all elements properly. The key part of test code:
var $nameHeader = $list.find('.list-header .name'),
$nameCell = $list.find('.list-body .name');
ok($nameHeader.hasClass('ui-resizable'), 'Name header should be resizable');
equal($nameCell.outerWidth(), 300, 'Initial width of Name column');
// retrieve instance of resizable widget
var instance = $nameHeader.data('ui-resizable'),
position = $nameHeader.position(),
width = $nameHeader.outerWidth();
ok(instance, 'Instance of resizable widget should exist');
// mouseover initializes instance.axis to 'e'
instance._handles.trigger('mouseover');
// start dragging, fires `start` callback
instance._mouseStart({pageX: position.left + width, pageY: position.top});
// drag 50px to the right, fires `resize` callback
instance._mouseDrag({pageX: position.left + width + 50, pageY: position.top});
// stop dragging, fires `stop` callback
instance._mouseStop({pageX: position.left + width + 50, pageY: position.top});
// ensure width of linked element is changed after resizing
equal($nameCell.outerWidth(), 350, 'Name column width should change');
Of course this code is brittle and may break when widget implementation changes.
Hack Disclaimer (tested on jQuery 1.12.4):
This basically waits for the dialog to be opened and then increments by 1px (which forces the resize() event) and then decrements by 1px (to regain original size)
just say this in the dialog open event handler:
$(this)
.dialog("option","width",$(this).dialog("option","width")+1)
.dialog("option","width",$(this).dialog("option","width")-1);
note:
This may not work with show effects (like fadeIn,slideDown etc) as the "resizer" code executes before the dialog is fully rendered.
$(".yourWindow").each(function(e) {
$(this).height($(this).find(".yourContent").height());
});
And the same with the width.

Is there a way of dynamically toggling the Highstock navigator to regain vertical space for the chart?

I'd like to be able to be able to dynamically toggle the presence of the Highstock navigator and allow the chart to expand into the vertical space it occupied.
I've tried simply toggling chart.userOptions.navigator.enabled but it has no effect.
This thread explains how to use .hide() and .show() methods to conceal the individual components of the navigator and scrollbar, but these use visibility:hidden so the space does not become available for the chart. However, using .css({display: 'none'}) works, but the series itself has no .css() method, and I've been unable to find a way of removing the series from just the navigator.
Does anyone know a method to achieve what I want?
Thanks.
In short: it's not supported to hide navigator in real time. The best way would be to destroy chart and create new one with disabled navigator.
Other solution is to use workaround provided by Sebastian Bochan. Then you will need to update manually yAxis.height, for example: http://jsfiddle.net/dJbZT/91/
$('#btn').toggle(function () {
chart.yAxis[0].defaultHeight = chart.yAxis[0].height;
chart.xAxis[0].defaultHeight = chart.xAxis[0].height;
chart.yAxis[0].update({
height: 500 - chart.plotTop - 35
}, false);
chart.xAxis[0].update({
height: 500 - chart.plotTop - 35
});
chart.scroller.xAxis.labelGroup.hide();
chart.scroller.xAxis.gridGroup.hide();
chart.scroller.series.hide();
chart.scroller.scrollbar.hide();
chart.scroller.scrollbarGroup.hide();
chart.scroller.navigatorGroup.hide();
$.each(chart.scroller.elementsToDestroy, function (i, elem) {
elem.hide();
})
}, function () {
chart.yAxis[0].update({
height: chart.yAxis[0].defaultHeight
}, false);
chart.xAxis[0].update({
height: chart.xAxis[0].defaultHeight
});
chart.scroller.xAxis.labelGroup.show();
chart.scroller.xAxis.gridGroup.show();
chart.scroller.series.show();
chart.scroller.navigatorGroup.show();
chart.scroller.scrollbar.show();
chart.scroller.scrollbarGroup.show();
$.each(chart.scroller.elementsToDestroy, function (i, elem) {
elem.show();
})
});
There's another way to do this: reduce the chart height by the navigator's height, and set chart.reflow to false to prevent the Y-axis from adapting to the new chart height (try setting it to true in the fiddle -- notice the flicker when you show / hide the navigator?).
I've added this answer to the other thread, and the demo is here: http://jsfiddle.net/dJbZT/148/ (credits to Sebastian Bochan for the original answer).
I'm not sure when highcharts added this ability via options, but this worked for me:
var chart = $('#graphContainer').highcharts();
chart.options.navigator.enabled = !chart.options.navigator.enabled;
$('#graphContainer').highcharts(chart.options);

HighCharts Keep Vertical Line on Click Event

Using this example: http://jsfiddle.net/gh/get/jquery/1.7.2/highslide-software/highcharts.com/tree/master/samples/stock/demo/candlestick-and-volume/
When you hover over points on the chart, you get a nice vertical line showing you which point you're currently on. I want to modify the click event so that the vertical line stays when I hover away after a click. Changing the line color would be ideal on click, but not necessary.
If I click another point I'd want to remove any previous lines. Any ideas on how I could accomplish this?
The above solution like I said, is really cool, but is kind of a hack (getting the path of the crosshair) into the implementation details of highcharts, and may stop working in future releases, may not be totally cross browser (esp since <IE8 do not support SVG, the adding path may still work as it should be handled by highchart's add path method, but getting the crosshair's path may not work, I may be wrong, am an SVG noob). So here I give you the alternate solution of dynamically adding plotLines. PlotLines also allow some additional features like dashStyles, label etc.
get the axis and x value of point clicked (may not exactly overlap the crosshair)
var xValue = evt.xAxis[0].value;
var xAxis = evt.xAxis[0].axis;
Or
EDIT If you want to have the plotLine at the location of the crosshair and not the click position, you can use following formula (No direct API to get this, obtained from source code hence may stop working if code changes)
var chart = this;
var index = chart.inverted ? chart.plotHeight + chart.plotTop - evt.chartY : evt.chartX - chart.plotLeft;
var xValue = chart.series[0].tooltipPoints[index].x;
Add plotline
xAxis.addPlotLine({
value: xValue,
width: 1,
color: 'red',
//dashStyle: 'dash',
id: myPlotLineId
});
You can cleanup existing plotline
$.each(xAxis.plotLinesAndBands,function(){
if(this.id===myPlotLineId)
{
this.destroy();
}
});
OR
try {
xAxis.removePlotLine(myPlotLineId);
} catch (err) {}
Putting the pieces together
var myPlotLineId="myPlotLine";
...
var chart=this;
index = chart.inverted ? chart.plotHeight + chart.plotTop - evt.chartY : evt.chartX - chart.plotLeft;
var xValue = chart.series[0].tooltipPoints[index];
// var xValue = evt.xAxis[0].value; // To use mouse position and not crosshair's position
var xAxis = evt.xAxis[0].axis;
$.each(xAxis.plotLinesAndBands,function(){
if(this.id===myPlotLineId)
{
this.destroy();
}
});
xAxis.addPlotLine({
value: xValue,
width: 1,
color: 'red',
//dashStyle: 'dash',
id: myPlotLineId
});
...
Add plot lines at click position # jsFiddle
Persist crosshair/cursor as plot lines on click # jsFiddle
You can do it in several ways
Highchart has a very cool renderer that allows you to add various graphics to the chart. One of the options is to add a path I will be illustrating the same here.
We shall reuse the path of the crosshair and add the same to the chart with some additional styles like color you mentioned. The path of the crosshair can be optained as this.tooltip.crosshairs[0].d this is in string form and can be easily converted to an array using the Array.split() function
click: function() {
this.renderer.path(this.tooltip.crosshairs[0].d.split(" ")).attr({
'stroke-width': 2,
stroke: 'red'
}).add();
}
This will accomplish adding the line. You can store the returned object into a global variable and then when you are about to add another such line, you can destroy the existing one by calling Element.destroy()
var line;
...
chart:{
events: {
click: function() {
if (line) {
line.destroy();
}
line = this.renderer.path(this.tooltip.crosshairs[0].d.split(" ")).attr({
'stroke-width': 2,
stroke: 'red'
}).add();
}
}
...
Persist tooltip / crosshair on click # jsFiddle
Assuming you don't have much meta data to be shown along with the line, this is the easiest (or the coolest :) ) approach. You can also attach meta data if you want to using the renderer's text object etc.
An alternate way could be adding vertical plotLines to the xAxis
UPDATE
Refer my other solution to this question, that would work with zoom,scroll,etc

Using JqueryUI slider to range filter Datatable results

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 :)

Resources