Tabulator v3.5.3
Electron Desktop app (OSX & PC)
With 300+ items, Tabulator is exhibiting odd behavior; most serious is that items are disappearing. Other symptoms include the scroll "thumb" size and position becoming erratic and inconsistent (e.g thumb position not reflecting scroll position in overall list).
I'm wondering if I set the virtualDomBuffer too high or too low. The behavior is occurring when it is set to 300. Only 13-15 items will ever appear on-screen at once. I am setting the height of the Tabulator instance thus: height: "84vh". Is that a valid CSS value that Tabulator can handle?
I've tried setting virtualDomBuffer to 1000 but maybe that is the wrong way to handle this?
(I know there is a newer version of Tabulator but I am needing to support and existing app and don't want to introduce a big change unless I have to.
- - - -
Tabulator instance initialization
eventList = $("#event-list");
eventList.tabulator({
height: "84vh",
virtualDomBuffer: 300,
layout: "fitColumns",
resizableColumns: false,
selectable: 1,
responsiveLayout: "hide",
placeholder: "No Events",
columns: [
{
title: "Events",
field: "startDate",
formatter: eventListRowFormatter,
variableHeight: true,
headerSort: false
},
{ formatter: deleteIcon, width: 5, align: "center", cellClick: deleteFromEventList }
],
rowClick: function (e, row) {
// prevent deselecting clicked row
var selectedRows = eventList.tabulator("getSelectedRows");
if (selectedRows.length == 0) {
eventList.tabulator("selectRow", row);
} else if (isEventDirty == false) {
lastEventSelected = row;
setActiveEvent(row.row.data)
} else {
eventList.tabulator("deselectRow");
eventList.tabulator("selectRow", lastEventSelected);
}
},
rowDeselected: function (row) {
if (isEventDirty == true) {
lastEventSelected = row;
}
},
ajaxLoader: false
});
Column formatter
function eventListRowFormatter(cell) {
var data = cell.cell.row.data
if (data.eventTitle == "") {
return " - " + "<br>" + "<small>" + data.startDate + "</small>"
} else {
return data.eventTitle + "<br>" + "<small>" + data.startDate + "</small>"
}
}
There were some rendering glitches in version 3.5, i would suggest updating to the 4.1 release
Related
I have a DataTable which has a couple of thousand records in it.
I have the responsive plugin for it, and the responsive option is enabled.
I also tried enabling the deferRender option, but this appeared to have no impact on the time taken.
When I resize the browser there is a delay of 1s - 2s. This happens in IE11, and MS Edge. The performance in chrome isn't fantastic, but at 0.5s it's tolerable.
I am using custom ordering functions, but those functions are omitted for brevity. I'm fairly sure I know where the issue is, and it's not in them. I can provide them if required.
Here's my initialisation code:
this._dataTable = $("#listtable").DataTable({
paging: true,
responsive: true,
deferRender: true,
columns: [{
title: "Name",
data: "thing.name"
}, {
title: "State 1",
data: "state1",
type: "state1",
render: (data, type, row, meta) => {
return this._renderState1(data, meta);
}
}, {
title: "State 2",
data: "state2",
type: "state2",
render: (data, type, row, meta) => {
return this._renderState2(data, meta);
}
}]
});
I load the data by calling dataTable.row.add for each item, and then calling dataTable.draw at the end.
The performance issues occur after all the data has been successfully loaded, so I don't think it's to do with that.
Digging further in to the profiler information I found that it was the rendering of the rows that was the issue:
By commenting out code in my custom render functions shown in the initalisation code, I found that the issue lay with finding the containing cell to set the background colour:
var cell = this._dataTable
.cell({ row: meta.row, column: meta.col })
.node();
Here's the rest of the code for setting the background colour:
var cellClass = this._getStateClass(state);
$(cell).addClass(cellClass);
If I comment the cell retrieval line out then the performance isn't amazing, but it is acceptable.
So my question is how can I have a custom background colour for cells while maintaing the responsive performance?
A fast alternative to dataTable.cell would do, as would an alternative approach to setting the background colour.
I managed to solve this issue by removing the need to find the cell.
I put a class on the columns that removed the padding they have.
Style:
.cell-state1 {
padding: 0;
}
Configuration:
this._dataTable = $("#listtable").DataTable({
paging: true,
responsive: true,
deferRender: true,
columns: [{
title: "Name",
data: "thing.name"
}, {
title: "State1",
data: "state1",
type: "state1",
className: "cell-state1",
render: (data, type, row, meta) => {
return this._renderState1(data, meta);
}
}]
});
Then I changed my render functions so they returned the content in a div which filled the cell, had the background colour, and added the padding back in.
Style:
.cell-state1-somestate {
height: 100%;
width: 100%;
padding: 8px;
background-color: #000000;
color: #ffffff;
}
Render Function:
function _renderState1 (state1) {
var cssClass = _this._getState1CellClass(state1);
var text = _this._getState1CellText(state1);
var content = "<div class='" + cssClass + "'>" + text + "</div>";
return content;
};
This left me with one final issue.
I have custom order functions, and now rather than being passed the text value they're passed the div containing the text value.
I used a little bit of jQuery to extract the text:
var floodAlertSeverity = $(content).html();.
It'd be nice if the order functions received the original data, rather than the rendered data, but oh well.
I had a similar issue with IE11 becoming extremely slow when resizing the browser window in responsive mode, which made for a pretty terrible user experience.
I don't have the time nor the expertise to fix the underlying issue (probably just IE11 being slow), but I came up with an elegant hack to work around the problem, which is basically to throttle the calls to the function that adjusts the column sizes.
The performance profiler in IE11 showed that calls to _fnAdjustColumnSizing(oSettings); were taking most of the CPU time, and the calls to this method are triggered by the 'resize.DT-YourTableNameHere' event, so using a simple timer we can delay the call to this function until the user is done resizing the window:
var dtResizeTimer;
var allowPropagation = false;
$(window).on("resize.DT-visitsTable", function (event) {
if (allowPropagation === false) {
event.stopImmediatePropagation();
clearTimeout(dtResizeTimer);
dtResizeTimer = setTimeout(function() {
allowPropagation = true;
$(window).trigger("resize.DT-visitsTable");
}, 100);
} else {
allowPropagation = false;
}
});
Obviously you'll need to replace visitsTable with whatever id you gave to your table element.
It still takes a second for IE11 to update the table after the user is done resizing, but at least the resizing itself is now smooth. There might be a better solution, but for now, this calms my frustration with IE11.
I'm trying to render two dynamic charts using canvasJS, Chart one is rendering properly, in fact second chart is populating properly, please see the inspect element image, but it's not displaying on the page.
This is interface, just one chart is displaying
and here is inspect elem.
#adverts is rendering, but not displayed here.
.chartContainer{
height: 400px;
width: 100%;
}
PS I tried to commented out first chart rendering code, but not working at all, still displaying the first one only.
EDIT
Here is console log about what I'm getting back to render chart
here is snippet
function drawChart(obj, placeholder, From, To){
var legendFrom;
var legendTo;
if(From != 'undefined'){
legendFrom = From;
}
if(To != 'undefined'){
legendTo = To;
}
var dataPoints = [];
var from = [];
var to = [];
var advertFrom = [];
var advertTo = [];
for (var i = 0; i <= obj.length - 1; i++) {
if(obj[i].typeOf != 'undefined' || obj[i].typeOf != ''){
if(obj[i].typeOf == 'from'){
from.push({label:obj[i].year+"-"+obj[i].month+"-"+obj[i].day,y:Math.round(obj[i].avgPrice * 1000) / 1000});
advertFrom.push({label:obj[i].year+"-"+obj[i].month+"-"+obj[i].day,y:obj[i].adverts});
}else{
to.push({label:obj[i].year+"-"+obj[i].month+"-"+obj[i].day,y:Math.round(obj[i].avgPrice * 1000) / 1000});
advertTo.push({label:obj[i].year+"-"+obj[i].month+"-"+obj[i].day,y:obj[i].adverts});
}
}
else
from.push({label:obj[i].year+"-"+obj[i].month+"-"+obj[i].day,y:Math.round(obj[i].avgPrice * 1000) / 1000});
}
var chart = new CanvasJS.Chart("chartContainer",
{
animationEnabled: true,
//theme: "theme1",
zoomEnabled: true,
title:{
text: placeholder
},
data: [
{
type: "spline", //change type to bar, line, area, pie, etc
showInLegend: true,
legendText: "From "+legendFrom,
dataPoints: from
},
{
type: "spline",
showInLegend: true,
legendText: "From "+legendTo,
dataPoints: to
}
],
legend: {
cursor: "pointer",
itemclick: function (e) {
if (typeof(e.dataSeries.visible) === "undefined" || e.dataSeries.visible) {
e.dataSeries.visible = false;
} else {
e.dataSeries.visible = true;
}
chart.render();
}
}
});
chart.render();
var adverts = new CanvasJS.Chart("adverts",
{
animationEnabled: true,
zoomEnabled: true,
title:{
text: placeholder
},
data: [
{
type: "column", //change type to bar, line, area, pie, etc
showInLegend: true,
legendText: "From "+legendFrom,
dataPoints: advertFrom
},
{
type: "column",
showInLegend: true,
legendText: "From "+legendTo,
dataPoints: advertTo
}
]
});
adverts.render();
}
I just replicated a canvasJS enviroment in JSFiddle
http://jsfiddle.net/fLbngdv9/1/
Make sure you are are using a new VAR for each chart.
var chart = new CanvasJS.Chart("chartContainer", {..});
chart.render();
var chart2 = new CanvasJS.Chart("chartContainer2", {..});
chart2.render();
Seems to work fine. If you can provide me with some code examples then perhaps I can help you find an answer.
Probably just a copy paste mistake but you have a syntax error at the very bottom.
...
adverts.render();
}
This should be
...
adverts.render();
Just remove the extra bracket. I updated the JS Fiddle to be more like your example... [here]
Edit:
I was able to replicate in my latest JSFiddle and I know this sounds crazy but when I changed the ID of your second div from adverts to chartContainer2 it worked fine.
Still investigating as to why this is but I would try renaming the ID on the div and also the reference for the graphs creation.
SEE HERE
Edit 2:
This appears to be a Chrome specific bug with the ID adverts. To replicate all you need to do is check out this link http://www.briangebel.com/test.html
(As you can see the div Adverts is hidden in Chrome but visible on FF,Opera,IE)
So what is happening..
Chrome's ADBlocker extension sees this ID and automatically adds an in page stylesheet.
This style sheet has the following Style
This only affects Chrome as it is caused by a specific extension. (I am attempting to contact the developer to see if he can resolve this but until then follow the below solution)
Solution
Simple don't use that ID. (You could simply disable AdBlocker but this extension is so widely used I would still recommend changing the ID)
I need to add a jquery ui slider to each cell of slickgrid. Number of records is over 10,000 with over 150 columns. The problem is that the initial set of sliders are rendered fine but as I scroll (left or right), they disappear. Somehow, the grid is not getting invalidated for those cells. I am using the following formatter on the column:
SliderFormatter = function (row, cell, value, colDef, dataContext) {
var html = "<div class='mySlider' id='slider_" + dataContext['id'] + "'></div>" + value;
return html;
}
and invoking the slider from my document.ready callback.
Any help will be appreciated. Thanks in advance !
SlickGrid renders only what's visible in the viewport, plus a small buffer. As you scroll, rows/cells are dynamically added and removed.
What this means for your case, is that when you initialize the slider widget in your document.ready callback, you're only initializing a tiny portion of them, and the ones that did get initialized, don't get re-initialized when the cells they are in are removed and recreated by SlickGrid.
SlickGrid doesn't allow you to use jQuery widgets like the slider in cells and requires that formatters output pure HTML in order to make it hard to implement the grid in a way that will slow it down. (I won't get into my reasoning behind that admittedly controversial decision.)
My advice here is to avoid using the jQuery UI slider here. It is simply not scalable or performant enough. Without virtualization, what you're trying to do is impossible - initializing 100 sliders is going to be really slow; initializing 10'000 x 150 of them is out of the question. With virtualization, you'll need to initialize and destroy them on the fly as you're scrolling around, and that's just too slow to scroll smoothly.
Consider using an HTML5 range input - <INPUT type="range">. It's supported by all major browsers with the exception of IE <10. See http://caniuse.com/#feat=input-range.
I've created an example using SlickGrid's async post-render option. #Tin is probably right that it would be better to use the native <input type="range"> but just in case you need to support older browsers here's how you can do it.
function waitingFormatter(value) {
return '<div class="slider"></div>';
}
function renderSlider(cellNode, row, dataContext, colDef) {
var cell = $(cellNode);
cell.find('.slider').slider({
value: dataContext.value,
slide: function(e, ui) {
data[row].value = ui.value;
cell.prev().html(ui.value);
}
});
}
var grid;
var data = [];
var columns = [
{ id: "title", name: "Title", field: "title", sortable: false, width: 120, cssClass: "cell-title" },
{ id: "value", name: "Value", field: "value", sortable: false, editor: Slick.Editors.Integer, width: 40, validator: requiredFieldValidator },
{ id: "chart", name: "Slider", sortable: false, width: 425, formatter: waitingFormatter, rerenderOnResize: true, asyncPostRender: renderSlider }
];
var options = {
editable: true,
enableAddRow: false,
enableCellNavigation: true,
asyncEditorLoading: false,
enableAsyncPostRender: true
};
$(function () {
for (var i = 0; i < 500; i++) {
var d = (data[i] = {});
d["title"] = "Record " + i;
d["value"] = Math.round(Math.random() * 100);
}
grid = new Slick.Grid("#myGrid", data, columns, options);
})
My problem: When I drag a row in jqGrid, and it completes a custom reload function, the cells of the grid, previously all of varying widths set when the grid is defined, are resized to all be the same width. This happens in Webkit browsers but not in Firefox.
Code:
I have dragging to sort enabled on a grid:
$mygrid.jqGrid(
'sortableRows', {
update: function(e, ui) {
sort_grid(e, ui);
}
}
);
As you can see I have a sorting function called on drag complete, sort_grid. Here it is:
function sort_grid(e, ui) {
var current_grid = $(ui.item[0]).closest('table').attr('id');
var $current_row, moved_id, next_id, next_priority;
var $moved_row = $('#' + current_grid + ' tr');
var cnt = 0;
this_id = ui.item[0].id;
$moved_row.each(function () {
if ($(this).attr('id') == this_id) {
$current_row = $moved_row.eq(cnt);
moved_id = $current_row.attr("id");
next_id = $current_row.next().attr("id");
next_priority = $current_row.next().children("td:first").attr("title");
}
cnt++;
});
if ( typeof moved_id !== 'undefined' ) {
if ( next_priority == 'undefined' ) {
next_priority = '999';
}
$.ajax({
url:my_url,
type:"POST",
data:"moved_id=" + moved_id + "&next_id=" + next_id + "&next_priority=" + next_priority,
success: function(data) {
$('.grid').setGridParam({loadonce:false, datatype:'json'}); // force grid refresh from server
$('#' + current_grid).trigger("reloadGrid");
$('.grid').setGridParam({loadonce:true}); // reset to use local values
}
})
}
}
Once I hit that reload trigger $('#' + current_grid).trigger("reloadGrid"); and reload finishes the grid now has incorrect widths on the cells in the grid (they go from being of various widths to all being the same width).
When the grid was originally created it had widths defined in the normal jqGrid fashion:
colModel:[
{name:'one', index:'one', sortable:true, width:45},
{name:'two', index:'two', sortable:true, width:180},
]
but after the grid reload the widths are reset all be the same width (I assume this is the total width of the grid being evenly divided over the total number of cells in the row). So, do I need to explicitly set these widths again, perhaps with something like the following called after the grid reloads?
$('.grid').setGridParam({
colModel:[
{name:'one', index:'one', sortable:true, width:45},
{name:'two', index:'two', sortable:true, width:180},
]
});
I tried the above fix, redefining the colModels after reload and explicitly setting the widths, but it had no effect. Weirder, if I go into the browser console and set the widths with javascript it also has no effect. That's got me stumped.
Unfortunately for me it looks like the jqGrid "Answer Man" (Oleg) is not around... lol.
I faced the same problem for Chrome. I recreated it here http://jsfiddle.net/gZSra/. Just drag the row and then sort any column.
But after few hard hours of debugging jqGrid sources I finally fixed this bug. The problem appears in emptyRows method of jqGrid.
emptyRows = function (scroll, locdata) {
var firstrow;
if (this.p.deepempty) {
$(this.rows).slice(1).remove();
} else {
firstrow = this.rows.length > 0 ? this.rows[0] : null;
$(this.firstChild).empty().append(firstrow); // bug "activation" line
}
if (scroll && this.p.scroll) {
$(this.grid.bDiv.firstChild).css({height: "auto"});
$(this.grid.bDiv.firstChild.firstChild).css({height: 0, display: "none"});
if (this.grid.bDiv.scrollTop !== 0) {
this.grid.bDiv.scrollTop = 0;
}
}
if(locdata === true && this.p.treeGrid) {
this.p.data = []; this.p._index = {};
}
},
*In recent jqGrid-4.4.4 this code begins from 1070 line of jqGrid.src.js
The problem connected removing and then appending firstrow. This row defines width of columns - one of it's cells in my jsFiddle example is
<td role="gridcell" style="height:0px;width:60px;"></td>
That is why problem seems to be connected with some Chrome's or Webkit's dynamic table behaviour.
FIX
Replace infected else scope with next line
$(this.firstChild).find('tr:not(:first)').remove();
It's not hard to see that instead of removing all lines and then appending first back, I just selecting and removing all except first row.
Result jsFiddle: http://jsfiddle.net/HJ3Q3/
Tested in Chrome, FF, IE 8 & 9.
Hope this fix will soon become part of jqGrid sources.
.trigger('reloadGrid');
is causing issues with sortablerows.
Below work around might help you (Unload & reload grid)
Create a function to configure grid, like below
jQuery().ready(ConfigureGrid);
function ConfigureGrid(){
jQuery("#grdCategory").jqGrid({
url: '/controller/action',
datatype: "xml",
colNames: ['Order', 'Name', 'Page Title', 'Is Active', 'Action'
],
colModel: [
{ name: 'col1', index: 'col1', width: 50, sortable: true, sorttype: 'int' }
, { name: 'col2', index: 'col2', width: 150, sortable: true }
],
rowNum: 10,
rowList: [10, 20, 30],
});
$("#list1").jqGrid('navGrid', '#pager1', { edit: false, add: false, del: false, search: true });
$("#list1").jqGrid('sortableRows', { update: function (event, ui) { updateOrder() } });
}
Create function to reload grid
function loadGrid() {
$('#grdCategory').jqGrid('GridUnload');
ConfigureGrid();
}
use loadGrid() function in ajax call back or to refresh grid
Have you tried to setup this property in your jQgrid options
width: 'auto',
If this doesnt work try reloading your grid after the update of the row
jQuery('.grid').trigger('reloadGrid');
The comment of PokatilovArt needs more attention : jqGrid - Dragging a row to sort it screws up cell widths .
It solves the problem in Chrome.
Here is the parameter to change in jqGrid :
deepempty : true
In jqGrid wiki, here is the definition of this option.
This option should be set to true if an event or a plugin is attached to the table cell. The option uses jQuery empty for the the row and all its children elements. This of course has speed overhead, but prevents memory leaks. This option should be set to true if a sortable rows and/or columns are activated.
I want to write my own layout.. (like vbox, border and so one)... What i want to do is to create layout that will place it's content in the middle (verticall - middle, horisontal - middle)..
Is there some one who could show me how this control will look like in extJs or can provide some links that may be usefull?
I have this example from
http://dev.sencha.com/deploy/dev/examples/layout-browser/layout-browser.html
Ext.ux.layout.CenterLayout = Ext.extend(Ext.layout.FitLayout, {
// private
setItemSize : function(item, size){
this.container.addClass('ux-layout-center');
item.addClass('ux-layout-center-item');
if(item && size.height > 0){
if(item.width){
size.width = item.width;
}
item.setSize(size);
}
}
});
Ext.Container.LAYOUTS['ux.center'] = Ext.ux.layout.CenterLayout;
But it gives me more questions than answers.. What is setItemSize How it works? When? Why? ect. What is item.setSize called for? How it works? When? Why? ect.
check the examples of ExtJS 3, there is an Ext.ux.Layout.CenterLayout under custom layouts there already, maybe a good point to start?
http://dev.sencha.com/deploy/dev/examples/layout-browser/layout-browser.html
Edit: Try this layout user extension, it centers an item in the horizontal and vertical center of it's container
Ext.ns('Ext.ux.layout');
Ext.ux.layout.CenterLayout = Ext.extend(Ext.layout.ContainerLayout, {
monitorResize:true,
type: 'ux.center',
getLayoutTargetSize : function() {
var target = this.container.getLayoutTarget();
if (!target) {
return {};
}
return target.getStyleSize();
},
onLayout : function(ct, target){
Ext.ux.layout.CenterLayout.superclass.onLayout.call(this, ct, target);
if(!ct.collapsed){
this.setItemSize(this.activeItem || ct.items.itemAt(0), this.getLayoutTargetSize());
}
},
setItemSize : function(item, size){
var left = (size.width - item.getWidth()) / 2;
var top = (size.height - item.getHeight()) / 2;
var pos = Ext.apply(item.getEl().getPositioning(), {
position : 'absolute',
left : left,
top : top
});
item.getEl().setPositioning(pos);
}
});
Ext.Container.LAYOUTS['ux.center'] = Ext.ux.layout.CenterLayout;
Ext.layout.ContainerLayout (the base class for layouts) has a doLayout method that triggers your container rendering.
In the case of FitLayout, its overriden and it calls a custom setItemSize function who calls the native setSize for the first container item (or the selected item if any), to fit the container size (maximize i guess).
Check also these custom layouts : http://layoutscroll.demo.revolunet.com/
Ext.create('Ext.container.Viewport', {
layout: {
type: 'hbox',
pack: 'center',
align: 'middle'
},
items: [
{
html: 'Hello World'
}
]
});