jQueryUI resizable - when you change the width of a column - all the other speakers twitch - jquery-ui

I use jQueryUI resizable for resizing columns in the table.
When you change the width of one column, the other columns must remain unchanged. Should only varies the width of the table
Why do you change the width of one column, while others twitch? If all columns to make the minimum width, and then begin to increase the width of the last column (right column), all the others begin to twitch.
<div class="table-wrapper">
<table id="table">
<tbody>
<tr>
<td class="gray">Zagreb</td>
<td class="gray">Kinshasa</td>
<td class="gray">Kishinev</td>
<td class="gray">Krakow</td>
<td class="gray">Lima</td>
<td class="gray">Lisbon</td>
</tr>
<tr>
<td>Zagreb</td>
<td>Kinshasa</td>
<td>Kishinev</td>
<td>Krakow</td>
<td>Lima</td>
<td>Lisbon</td>
</tr>
<tr>
<td>Zagreb</td>
<td>Kinshasa</td>
<td>Kishinev</td>
<td>Krakow</td>
<td>Lima</td>
<td>Lisbon</td>
</tr>
<tr>
<td>Zagreb</td>
<td>Kinshasa</td>
<td>Kishinev</td>
<td>Krakow</td>
<td>Lima</td>
<td>Lisbon</td>
</tr>
<tr>
<td>London</td>
<td>Los Angeles</td>
<td>Luxembourg</td>
<td>Madrid</td>
<td>Manila</td>
<td>Mexico</td>
</tr>
<tr>
<td>Milan</td>
<td>Montreal</td>
<td>Mumbai</td>
<td>Nairobi</td>
<td>Nicosia</td>
<td>New York</td>
</tr>
<tr>
<td>Osaka</td>
<td>Oslo</td>
<td>Ottawa</td>
<td>Paris</td>
<td>Prague</td>
<td>Riga</td>
</tr>
<tr>
<td>Rome</td>
<td>Rotterdam</td>
<td>Salvador</td>
<td>Samarkand</td>
<td>Sydney</td>
<td>Singapore</td>
</tr>
<tr>
<td>Sofia</td>
<td>Istanbul</td>
<td>Taipei</td>
<td>Tbilisi</td>
<td>Zurich</td>
<td>Chicago</td>
</tr>
</tbody>
</table>
</div>
JS code:
$(document).ready(function(e)
{
var $table = $("#table");
var startW = 0;
var startW_neighbor = 0;
var td_index_neighbor = 0;
var ui_minColWidth = 0;
$table.find("tr:first th, tr:first td").resizable(
{
handles: 'e',
minWidth: 30,
start : function(event,ui)
{
ui_minColWidth = $(this).resizable( "option", "minWidth");
startW = $(this).width();
startW_neighbor = ui.element.parents('.table-wrapper').find('table#table').width();
},
stop: function (event, ui)
{
},
resize: function (event, ui)
{
var td_width = ui.element.width();
var table = ui.element.parents('.table-wrapper').find('table#table');
var d = Number(td_width) - Number(ui.originalSize.width);
td_width = Number(startW_neighbor) + Number(d) - 2;
table.width(td_width);
}
});
});
In example: http://jsfiddle.net/djmartini/08p3Lqcm/

You're at the same time interfering with normal behavior of resizable and relying on it to calculate some values, so at some point it breaks. In that case you're calculating the width difference based on resizable behavior and applying it to change the behavior. But when resizable doesn't have any more place to resize, the difference is 0, hence nothing moves anymore.
If you want to change the behavior of resizable, you could work with mouse position and micro manage the change in width. In that case you'll need to handle cases where your mouse moves, but nothing gets resized. This happens when columns cannot reduce size anymore.
Something like this seems to work:
$table.find("tr:first th, tr:first td").resizable({
handles: 'e',
minWidth: 30,
start: function (event, ui) {
ui_minColWidth = $(this).resizable("option", "minWidth");
startW = $(this).width();
td_width = startW;//this to check if width can change
startW_neighbor = ui.element.parents('.table-wrapper').find('table#table').width();
startMouseX = event.pageX;//to calculate mouse diffrence
},
stop: function (event, ui) {
},
resize: function (event, ui) {;
var mouseDiff = event.pageX - startMouseX;//mouse mouvement
//If the mouse is negative, it can resize only if column
//isn't at min width. When mouse positive resize always.
if (ui.element.width() != td_width || mouseDiff > 0) {
td_width = ui.element.width();
var table = ui.element.parents('.table-wrapper').find('table#table');
var d = Number(td_width) - Number(ui.originalSize.width);
table_width = Number(startW_neighbor) + mouseDiff;
table.width(table_width);
}
}
});
fiddle: http://jsfiddle.net/wLbn5qjk/1/
EDIT:
You can push this logic and work with alsoResize, the result is a bit better:
$table.find("tr:first th, tr:first td").resizable({
handles: 'e',
minWidth: 30,
alsoResize: 'table',
start: function (event, ui) {
ui_minColWidth = $(this).resizable("option", "minWidth");
startW = $(this).width();
td_width = ui.element.width();
startW_neighbor = ui.element.parents('.table-wrapper').find('table#table').width();
startMouseX = event.pageX;
},
stop: function (event, ui) {
},
resize: function (event, ui) {;
var mouseDiff = event.pageX - startMouseX;
if (ui.element.width() == td_width && mouseDiff < 0) {;
$('table').width(table_width);
}
td_width = ui.element.width();
table_width = $('table').width()
}
});
http://jsfiddle.net/hrzj68wp/1/

Related

syncing jquery ui select menu with knockout observable array

probably going about this the wrong way but I have an html table that is populated using a knockout observable array using foreach. in each row I have a drop down. I like the jquery ui select menu so I am using that. unfortunately when you run the fiddle you will notice that when updating the drop down the corresponding knockout selected value is not being updated.
here is the fiddle https://jsfiddle.net/8rw4ruze/3/
here is the html.
<table class="table table-condensed table-responsive">
<thead>
<th>id</th>
<th>animal</th>
<th>Selected Value</th>
</thead>
<tbody data-bind="foreach: tableRows">
<tr>
<td data-bind="text: id"></td>
<td>
<select class="form-control" data-bind="options: recordList,
optionsText: 'desc',
optionsValue: 'val',
value: selectedVal,
attr: {id: selectId}">
</select>
</td>
<td data-bind="text: selectedVal"></td>
</tr>
</tbody>
</table>
and here is the javascript
function record(val, desc) {
var self = this;
this.val = ko.observable(val);
this.desc = ko.observable(desc);
}
function tableRow(id, recordList) {
var self = this;
this.id = ko.observable(id);
this.recordList = ko.observable(recordList)
this.selectedVal = ko.observable('A');
this.selectId = ko.computed(function() {
return 'mySelect' + self.id()
}, this);
}
function Model() {
var self = this;
this.records = ko.observableArray("");
this.tableRows = ko.observableArray("");
}
var mymodel = new Model();
$(function() {
mymodel.records.push(new record('A', 'ant'));
mymodel.records.push(new record('B', 'bird'));
mymodel.records.push(new record('C', 'cat'));
mymodel.records.push(new record('D', 'dog'));
mymodel.tableRows.push(new tableRow(1, mymodel.records()));
mymodel.tableRows.push(new tableRow(2, mymodel.records()));
mymodel.tableRows.push(new tableRow(3, mymodel.records()));
mymodel.tableRows.push(new tableRow(4, mymodel.records()));
ko.applyBindings(mymodel);
for (var i = 0; i < 4; i++) {
var id = '#mySelect' + (i + 1)
$(id).selectmenu({
width: 125,
change: function(event, ui) {
var newVal = $(this).val();
mymodel.tableRows()[i].selectedVal(newVal);
}
});
}
});
thanks all I went with a data attribute. I'd prefer to use the custom binding but I'm not smart enough to figure that out so I went with this.
for (var i = 0; i < 4; i++) {
var id = '#mySelect' + (i + 1)
$(id).selectmenu({
width: 125,
change: function(event, ui) {
var newVal = $(this).val();
var index = $(this).data( "index" );
mymodel.tableRows()[index].selectedVal(newVal);
}
}).data( "index", i );
}
here is the fiddle https://jsfiddle.net/8rw4ruze/7/
I think I got it working with the custom binding here it is
https://jsfiddle.net/8rw4ruze/8/
Matt.k is correct that your value of i is not what you want it to be when it is used. One way around that is to use this loop instead of a simple for:
$('select').each(function (i, e) {
e.selectmenu({
width: 125,
change: function(event, ui) {
var newVal = $(this).val();
mymodel.tableRows()[i].selectedVal(newVal);
}
});
});
This is a little fragile, as it relies on the i and the e to correspond correctly. A more robust (and typical in Knockout) approach is to use a binding handler, which would allow you to specify the binding and have Knockout go through the elements and make the necessary calls. Surprisingly, a little web-searching didn't turn one up for me.
I see there is a knockout-jqueryui project on github that might also give you a clean way of using the widgets you want.

Drag&Drop in jQuery on Table Cells

I have a table and need to drag/drop cells containing data into blank cells. I'm able to drag and drop fine, but once a cell is dragged away, I need its "old" position to now become a droppable cell. Since the cell is dragged, I need something else to reference. I ended up wrapping each of the td elements in a div element, but all references return "undefined" on the inner cellDiv id (or return the outer tableDiv id). On the fiddle, I notice that the blue background is not appearing so I don't think the 'cellDiv' is doing much.
Next I am going to try swapping the td and cellDiv elements, but I decided to post the question first, as I've searched everywhere and cannot seem to find this specific problem addressed. Thanks for your help.
here is the problem in a jsfiddle: http://jsfiddle.net/tgsz34hx/3/
And the code:
<div id='tableDiv'>
<table>
<tr id='row1'>
<div class='cellDiv' id='r1c1'>
<td class='drop' id='col1'>Drop Here</td></div>
<div class='cellDiv' id='r1c2'>
<td class='nodrop' id='col2'>Drag Me</td></div>
</tr>
</table>
</div>
$(document).ready(function () {
var fromDiv;
$('.nodrop').draggable({
cursor: "move",
appendTo: "body",
revert: "invalid",
start: function (event, ui) {
var thisDiv = $(this).attr('id');
fromDiv = $(this).closest('.cellDiv').attr('id');
alert("thisDiv=" + thisDiv + ", fromDiv=" + fromDiv);
}
});
$('.drop').droppable({
accept: ".nodrop",
tolerance: "pointer",
snap: ".drop",
drop: function (event, ui) {
var parenttd = $(ui.draggable).closest('td').attr('id');
var parentdiv = $(ui.draggable).closest('.cellDiv').attr('id');
// alert("parenttd=" + parenttd + ", parentdiv=" + parentdiv);
$(this).removeClass('drop');
$(this).addClass('nodrop');
$(this).droppable('option', 'disabled', true);
}
});
});
#tableDiv {
background-color: grey;
}
#tableDiv td {
background-color: red;
}
#tableDiv div {
background-color: blue;
}

jsPDF Table Width & Font Size

I thought this would be relatively simple but I cannot find an answer anywhere, I have had a good hunt in Stack Overflow. Apologies if I have missed something obvious!
How on earth do you set the table width in jsPDF? I simply want my table to stretch to 100% of the page width. Here is the code I am using:
<script>
function demoFromHTML() {
var pdf = new jsPDF('p', 'pt', 'a4');
source = $('#pdf')[0];
specialElementHandlers = {
'#bypassme': function (element, renderer) {
return true
}
};
margins = {
top: 80,
bottom: 40,
left: 40,
width: 600
};
pdf.fromHTML(
source,
margins.left,
margins.top, {
'width': margins.width,
'elementHandlers': specialElementHandlers
},
function (dispose) {
pdf.output('datauri');
}, margins);
}
</script>
I am using standard, unformatted HTML table.
<table class="reference">
<tbody><tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Points</th>
</tr>
<tr>
<td>Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td>94</td>
</tr>
<tr>
<td>John</td>
<td>Doe</td>
<td>80</td>
</tr>
<tr>
<td>Adam</td>
<td>Johnson</td>
<td>67</td>
</tr>
</tbody></table>
The table only stretches about 60% across the PDF, which just seems to be a random amount. In addition the text inside the table is a different font and size to the text outside the table.
Any help would be greatly appreciated.

Incorrect sorting in TableSorter

Strange behavoir in tablesorter:
$(document).ready(function() {
var timeInSec = function(node) { return $(node).attr("time") };
var sortDigit = { sorter : "digit" };
$("#agentRapport").tablesorter({
headers: {
1: sortDigit,
2: sortDigit
},
textExtraction: {
1: timeInSec,
2: timeInSec
},
sortList: [[1,1]]
});
});
Resulting sorted table looks like this (sorted the following myself like you would see it in the browser):
...
<tr>
<td>Banana</td>
<td time="1411252">23 min 31 sec</td>
<td time="352813">5 min 52 sec</td>
<td>4</td>
<td>225</td>
</tr>
<tr>
<td>Apple</td>
<td time="1391952">23 min 11 sec</td>
<td time="347988">5 min 47 sec</td>
<td>4</td>
<td>86</td>
</tr>
<tr>
<td>Plum</td>
<td time="1427192">23 min 47 sec</td>
<td time="356798">5 min 56 sec</td>
<td>4</td>
<td>119</td>
</tr>
<tr>
<td>Pear</td>
<td time="1381072">23 min 1 sec</td>
<td time="345268">5 min 45 sec</td>
<td>4</td>
<td>108</td>
</tr>
...
Is this a bug?
If you are using the original tablesorter (v2.0.5b) from tablesorter.com, the above code will not work since the textExtraction function will not allow the targetting of specific columns.
However, you can use my fork of tablesorter which does allow this option (demo)
The fork should work without any extra functions since it also has an alpha-numeric sort (demo):
$(function () {
$("#agentRapport").tablesorter({
sortList: [
[1, 1]
]
});
});
But if you want to continue using the original version of tablesorter, you can use a parser, and save yourself from adding a time="#" attribute to every cell (demo):
$.tablesorter.addParser({
id: "min&sec",
is: function (s) {
return false;
},
format: function (s, table) {
var min = (parseInt(s.match(/(\d+)(?:\s+min)/), 10) || 0) * 60,
sec = (parseInt(s.match(/(\d+)(?:\s+sec)/), 10) || 0);
return min + sec;
},
type: "numeric"
});
$(function () {
$("#agentRapport").tablesorter({
headers: {
1: { sorter: "min&sec" },
2: { sorter: "min&sec" }
},
sortList: [
[1, 1]
]
});
});

Kendo UI and knockout.js rowTemplate sorting

I have a MVC site where I use Kendo UI and knockout.js to display the pages. One scenario is to get the database information from server through $.getJSON and then display this information on a KendoUI grid.
<div data-bind="kendoGrid:{sortable:true, data:users, rowTemplate:'userRowTemplate'}>
<table>
<thead>
<tr>
<th>Username</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead> </table>
</div>
<script type="text/html">
<tr>
<td data-bind="text: Username"></td>
<td data-bind="text: FirstName"></td>
<td data-bind="text: LastName"></td>
<tr>
</script>
and the javascript :
<script type="text/javascript">
var ViewModel = function () {
var self=this;
self.users=ko.mapping.fromJS([]);
$getJSON("/UserManagementController/GetUsers",function(data){
ko.mapping.fromJS(data,{},self.users);
});
};
$(document).ready(function(){
var newViewModel=new ViewModel();
ko.applyBindings(newViewModel);
});
</script>
I want this data to be sortable on specific columns (the ones specified here are for example's sake), but I haven't been able to achieve this successfully. I have tried the solution from this knockout-kendo plugin issue post, which works well on simple objects, but does not work on observables. So my question is : how to map data from database through MVC controller to observables in knockout and displaying them in a Kendo grid, but still be able to sort them?
Thanks,
Alex Barac
You can do this by creating a JS view model to represent the data coming back from the server, and mapping the data into the view model. Then you can have simple objects that get set by subscribing to the matching observable properties to implement the sort.
Here is an example:
http://jsfiddle.net/R4Jys/1/
HTML:
<div data-bind="kendoGrid: gridOptions(myList)"></div>
<script id="rowTmpl" type="text/html">
<tr>
<td>
<span data-bind="text: firstName" />
</td>
<td>
<span data-bind="text: lastName" />
</td>
<td>
<span data-bind="text: userName" />
</td>
</tr>
</script>
JavaScript:
var gridItem = function () {
var self = this;
self.firstName = ko.observable();
self.firstNameSort;
self.firstName.subscribe(function (value) {
self.firstNameSort = value;
});
self.lastName = ko.observable();
self.lastNameSort;
self.lastName.subscribe(function (value) {
self.lastNameSort = value;
});
self.userName = ko.observable();
self.userNameSort;
self.userName.subscribe(function (value) {
self.userNameSort = value;
});
self.other = ko.observable('test');
return self;
};
var vm = function() {
var self = this;
self.myList = ko.observableArray();
self.test = ko.observable();
self.gridOptions = function (data) {
return {
data: data,
rowTemplate: 'rowTmpl',
useKOTemplates: true,
scrollable: true,
sortable: true,
columns: [
{
field: "firstNameSort",
title: "First Name",
width: 130
},
{
field: "lastNameSort",
title: "Last Name",
filterable: true
},
{
field: "userNameSort",
title: "Username"
}
]
}
};
var data = [{'firstName':'Steve', 'lastName':'Jones', 'userName': 'steve.jones'},
{'firstName':'Janet', 'lastName':'Smith', 'userName': 'janet.smith'},
{'firstName':'April', 'lastName':'Baker', 'userName': 'april.baker'},
{'firstName':'Dave', 'lastName':'Lee', 'userName': 'dave.lee'},
{'firstName':'Jack', 'lastName':'Bennet', 'userName': 'jack.bennet'},
{'firstName':'Chris', 'lastName':'Carter', 'userName': 'chris.carter'}];
self.myList(ko.utils.arrayMap(data, function(item) {
var g = new gridItem();
ko.mapping.fromJS(item, {}, g);
return g;
}));
};
var pageVm = new vm();
ko.applyBindings(pageVm);
As an alternative you can modify the kendo.web.js (version 2013.3.1119) on line 6844 & 6844.
Replace
compare: function(field) {
var selector = this.selector(field);
return function (a, b) {
a = selector(a);
b = selector(b);
With
compare: function(field) {
var selector = this.selector(field);
return function (a, b) {
a = ko.utils.unwrapObservable(selector(a));
b = ko.utils.unwrapObservable(selector(b));
By using the knockout utility "ko.utils.unwrapObservable" you can get the value of the observable for use when kendo compares the values of the column
DEFINE CUSTOM COMPARE FUNCTION will serves solution, where you can override the comparing function in field definition.
So you can do something like this:
$("#grid").kendoGrid({
dataSource: dataSource,
sortable: true,
columns: [{
field: "item",
sortable: {
compare: function(a, b) {
var valueA = a["item"];
var valueB = b["item"];
if (typeof valueA === "function") valueA = valueA();
if (typeof valueB === "function") valueB = valueB();
if (this.isNumeric(valueA) && this.isNumeric(valueB))
{
valueA = parseFloat(valueA);
valueB = parseFloat(valueB);
}
if (valueA && valueA.getTime && valueB && valueB.getTime)
{
valueA = valueA.getTime();
valueB = valueB.getTime();
}
if (valueA === valueB)
{
return a.__position - b.__position;
}
if (valueA == null)
{
return -1;
}
if (valueB == null)
{
return 1;
}
if (valueA.localeCompare)
{
return valueA.localeCompare(valueB);
}
return valueA > valueB ? 1 : -1;
}
}
}]
});
private isNumeric(input)
{
return (input - 0) == input && ('' + input).trim().length > 0;
}
The compare method is stolen from kendo script, but change is where the property is typeof function (what ko.observable is), it unwraps the value. Plus, I added support for numbers.

Resources