jQWidgets Forums
jQuery UI Widgets › Forums › Grid › Keyboard navigation slow
Tagged: slow navigation grid
This topic contains 13 replies, has 2 voices, and was last updated by Peter Stoev 10 years ago.
-
AuthorKeyboard navigation slow Posts
-
Hi there,
I’m using server side paging, editing, filtering and sorting. I have updated the the paging to allow for 10, 20, 50, 100, 1000 rows.I have 14 rows coming back in my table and 20 rows in the paginator. All ok so far. Keyboard navigation is not too bad.
I update the paginator to show 1000 rows. The same 14 rows are returned. selecting a cell, and pressing the down key takes about 4 seconds for the next cell to be selected. I have confirmed that there are no server calls being made so it’s nothing to do with the server latency. What can I check to try and speed it up.
Here is my code… sorry, its big.
var tradesSource = { datatype : "json", datafields : [ { name : 'id', type : 'string' }, { name : 'name', type : 'string' } ], id : 'id', cache : false, async : false, url : '/ajax/data/custom_jqxgrid_spec_item_trades.php', type : "POST", data : { user_lang : user_lang, app_domain : app_domain, user_token : user_token, confirmation_tag : getConfirmationTag() }, }; var tradesAdapter = new $.jqx.dataAdapter(tradesSource, { autoBind : true }); // logger("tradesAdapter:"); // logger(tradesAdapter); var subcontractorsSource = { datatype : "json", datafields : [ { name : 'id', type : 'string' }, { name : 'name', type : 'string' } ], id : 'id', cache : false, async : false, url : '/ajax/data/custom_jqxgrid_spec_item_subcontractors.php', type : "POST", data : { user_lang : user_lang, app_domain : app_domain, user_token : user_token, confirmation_tag : getConfirmationTag() }, }; var subcontractorsAdapter = new $.jqx.dataAdapter(subcontractorsSource, { autoBind : true }); // logger("subcontractorsAdapter:"); // logger(subcontractorsAdapter); var reportGroupsSource = { datatype : "json", datafields : [ { name : 'id', type : 'string' }, { name : 'name', type : 'string' } ], id : 'id', cache : false, async : false, url : '/ajax/data/custom_jqxgrid_spec_item_report_groups.php', type : "POST", data : { user_lang : user_lang, app_domain : app_domain, user_token : user_token, confirmation_tag : getConfirmationTag() }, }; var reportGroupsAdapter = new $.jqx.dataAdapter(reportGroupsSource, { autoBind : true }); // logger("reportGroupsAdapter:"); // logger(reportGroupsAdapter); var dataSource = { unboundmode: true, url : '/ajax/data/custom_jqxgrid_spec_items.php', type : "POST", dataType : "json", cache : false, id : 'id', datafields : [ { name : 'id', type: 'int' }, { name : 'page_number', type: 'string' }, { name : 'spec_notation', type: 'string' }, { name : 'name', type: 'string' }, { name : 'trade_id', type: 'string', values : { source : tradesAdapter.records, value : 'id', name : 'name' } }, { name : 'subcontractor_id', type: 'string', values : { source : subcontractorsAdapter.records, value : 'id', name : 'name' } }, { name : 'report_group_id', type: 'string', values : { source : reportGroupsAdapter.records, value : 'id', name : 'name' } }, { name : 'labour_markup', type: 'float' }, { name : 'quantity', type: 'int', }, { name : 'labour_cost', type: 'float' }, { name : 'cost', type: 'float' }, { name : 'markup', type: 'float' }, { name : 'total', type: 'float' }, { name : 'additional_item', type : 'bool' }, { name : 'omit', type: 'float' }, { name : 'addition', type: 'float' }, { name : 'percentage_done', type: 'int' }, { name : 'work_complete', type: 'float' } ], //data : { // user_lang : user_lang, // app_domain : app_domain, // user_token : user_token, // confirmation_tag : getConfirmationTag(), // spec_id : $('#list_spec_items_spec_id').val() //}, filter : function() { // update the grid and send a request to the server. $("#jqxgrid_spec_items").jqxGrid('updatebounddata', 'filter'); }, sort : function() { // update the grid and send a request to the server. $("#jqxgrid_spec_items").jqxGrid('updatebounddata', 'sort'); }, root : 'Rows', beforeprocessing : function(data) { if (data != null) { //logger("beforeProcessing() got totalRows = '" + data[0].TotalRows + "'"); dataSource.totalrecords = data[0].TotalRows; $("#manage_spec_items_delete_button").hide(); $("#jqxgrid_spec_items").show(); } }, deleterow : function(rowid, commit) { //logger("got delete for id '" + rowid +"'"); //return; // synchronize with the server - send delete command $.ajax({ url : '/ajax/data/custom_jqxgrid_spec_items.php', type : "POST", dataType : 'json', cache : false, data : { user_lang : user_lang, app_domain : app_domain, user_token : user_token, confirmation_tag : getConfirmationTag(), delete : true, id : rowid, }, success : function(data, status, xhr) { // delete command is executed. commit(true); jqx_spec_items_selected_row = null; $("#manage_spec_items_delete_button").hide(); }, error : function(jqXHR, textStatus, errorThrown) { commit(false); } }); }, updaterow : function(rowid, rowdata, commit) { $.ajax({ url : '/ajax/data/custom_jqxgrid_spec_items.php', type : "POST", dataType : 'json', cache : false, data : { user_lang : user_lang, app_domain : app_domain, user_token : user_token, confirmation_tag : getConfirmationTag(), update : true, id : rowdata.id, page_number : rowdata.page_number, spec_notation : rowdata.spec_notation, name : rowdata.name, trade_id : rowdata.trade_id, subcontractor_id : rowdata.subcontractor_id, report_group_id : rowdata.report_group_id, labour_markup : rowdata.labour_markup, quantity : rowdata.quantity, labour_cost : rowdata.labour_cost, additional_item : rowdata.additional_item, omit : rowdata.omit, addition : rowdata.addition, percentage_done : rowdata.percentage_done, }, success : function(data, status, xhr) { // update command is executed. commit(true); }, error : function() { // cancel changes. commit(false); } }); } }; var dataAdapter = new $.jqx.dataAdapter(dataSource, { formatData: function (data) { $.extend(data, { user_lang : user_lang, app_domain : app_domain, user_token : user_token, confirmation_tag : getConfirmationTag(), spec_id : $('#list_spec_items_spec_id').val() }); return data; } }); // initialize jqxGrid $("#jqxgrid_spec_items").jqxGrid({ selectionmode: 'multiplecellsadvanced',width : "100%", columnsheight : 75, //selectionmode : 'singlecell', editmode : 'click', enablemousewheel:false, source : dataAdapter, sortable : true, filterable : true, //filtermode : 'excel', virtualmode: true, rendergridrows: function (params) { return params.data; }, pageable : true, pagesize : 20, pagesizeoptions : ['10','20','50','100','500','1000'], columnsresize : true, autoheight : true, autorowheight : true, editable : true, showtoolbar : true, localization: getLocalization(), rendertoolbar : function(toolbar) { // http://www.jqwidgets.com/community/topic/refresh-toolbar/ // $(this).jqxGrid('updatebounddata'); logger("Rendering toolbar"); var button = null; button = $("<input type='Button' class='manage_spec_item_action' id='manage_spec_items_refresh_button' value='Refresh List' onclick=\"manage_spec_items_refreshClicked();\"/>"); toolbar.append(button); button = $("<input type='Button' class='manage_spec_item_action' id='manage_spec_items_add_button' value='Add Spec Item' onclick=\"window.location = '#manage_spec_items_add_dialog';\"/>"); toolbar.append(button); button = $("<input type='Button' style='display:none;' class='manage_spec_item_action' id='manage_spec_items_delete_button' value='Delete Selected Spec Item' onclick=\"jqx_spec_items_delete();\"/>"); toolbar.append(button); }, columns : [ /*{ text : 'ID', datafield : 'id', }, */{ text : 'Page', datafield : 'page_number', width : 80 }, { text : 'Item', datafield : 'spec_notation', width : 80 }, { text : 'Description of the Spec Item', datafield : 'name', width : 300 }, { text : 'Trade', datafield : 'trade_id', width : 120, columntype : 'dropdownlist', createeditor : function(row, cellvalue, editor) { editor.jqxDropDownList({ displayMember : 'name', valueMember : 'id' }); }, initeditor : function(row, cellvalue, editor) { editor.jqxDropDownList({ source : tradesAdapter }); } }, { text : 'Subcontractor', datafield : 'subcontractor_id', width : 120, columntype : 'dropdownlist', createeditor : function(row, cellvalue, editor) { editor.jqxDropDownList({ displayMember : 'name', valueMember : 'id' }); }, initeditor : function(row, cellvalue, editor) { editor.jqxDropDownList({ source : subcontractorsAdapter }); } }, { text : 'Report Group', datafield : 'report_group_id', width : 120, columntype : 'dropdownlist', createeditor : function(row, cellvalue, editor) { editor.jqxDropDownList({ displayMember : 'name', valueMember : 'id' }); }, initeditor : function(row, cellvalue, editor) { editor.jqxDropDownList({ source : reportGroupsAdapter }); } }, { text : 'Markup (%)', datafield : 'labour_markup', width : 100, cellsformat: 'p', cellsalign: 'right' }, { text : 'Quantity', datafield : 'quantity', width : 100, cellsformat: 'n', cellsalign: 'center' }, { text : 'Unit Cost', datafield : 'labour_cost', width : 100, cellsformat: 'c2', cellsalign: 'right' }, { text : 'Net Price', datafield : 'cost', width : 100, cellsformat: 'c2', cellsalign: 'right', editable : false, }, { text : 'Net Markup', datafield : 'markup', width : 100, cellsformat: 'c2', cellsalign: 'right', editable : false, }, { text : 'Item Total', datafield : 'total', width : 100, cellsformat: 'c2', cellsalign: 'right', editable : false, }, { text : 'Additional', datafield : 'additional_item', width : 100, columntype : 'checkbox', }, { text : 'Omit', datafield : 'omit', width : 100, cellsformat: 'c2', cellsalign: 'right', }, { text : 'Addition', datafield : 'addition', width : 100, cellsformat: 'c2', cellsalign: 'right', }, { text : '% Complete', datafield : 'percentage_done', width : 100, cellsformat: 'p0', cellsalign: 'right' }, { text : 'Work Complete', datafield : 'work_complete', width : 100, cellsformat: 'c2', cellsalign: 'right', editable : false, } ] }); $("#jqxgrid_spec_items").bind('cellselect', function (event) { return; // just in case this is slowing stuff down... it's not. var row = event.args.rowindex; var datafield = event.args.datafield; var datarow = $("#jqxgrid_spec_items").jqxGrid('getrowdata', row); //logger("Selected row data:"); logger(datarow); jqx_spec_items_selected_row = event.args.rowindex; if(datarow.name.length > 75) { $("#manage_spec_items_delete_button").val("Delete Spec Item starting '" + datarow.name.substring(0,75) + "...'"); } else { $("#manage_spec_items_delete_button").val("Delete '" + datarow.name + "'"); } $("#manage_spec_items_delete_button").show(); });
Sorry, we cannot reproduce such behavior with current version. Example: http://www.jqwidgets.com/jquery-widgets-demo/demos/php/serverpaging.htm?arctic. The problem on your side is coming most probably of your “cellselect” handler which has a lot of code inside.
As you can see from my code the ‘cellselect’ returns immediately without doing anything. The example also doesn’t do keyboard navigation within cells, and doesn’t have 1000 as a page size option. If this were a “my code” issue then the behaviour would be the same regardless of which paging size since the same 14 rows are returned whether I chose 20/page or 1000/page. The only difference is the operational code that seems to do something more on the same data when I chose more per page – even when there is no more actual data. I have tried switching of the filtering and sorting, unbound mode and caching… all having the same issue.
That said, I have seen the cell navigation example, and spreadsheet like integration and it is one of the reasons we chose to purchase a commercial license (8B7FE2CB-0572-4A4C-88F5-49D6694293BF – assigned to my boss).
Is there anything else I can look at to try and work out where the slowness is coming from.
Hi Nigel,
1. don’t share your license key with anyone! I removed it from your post.
2. If you don’t like the sample I sent you, look at this one – http://www.jqwidgets.com/jquery-widgets-demo/demos/jqxgrid/virtualdata.htm?arctic – 1 Million Records loading on demand and Keyboard navigation definitely is not slow. Also 1000 records with autoheight = true – definitely will affect the performance because thousands of HTML Elements will have to be added to your page and browsers become slower with such amount of records. Use that without autoheight so the Grid will have to create HTML Elements only for the visible view.
3. Share jsfiddle.net sample which demonstrates your issue and make sure that you use jQWidgets 3.7.1.Oops, sorry for sharing my key.
These are indeed the culprit
autoheight : true, autorowheight : true,
They both seem to negatively impact performance, which surprised me for autorowheight.
That is interesting though, there are only 14 rows of data coming back, regardless of what the page size is set to. That seems like an optimisation issue? If I actually had 1000 rows of data coming back I could probably see how things may slow down initially, and then potentially working out which cell to move to next unless this was worked out prior to navigation.
I have disabled both options for now, hopefully something optimisely will come along
Thanks.
Hi Nigel
It should be expected the performance to degrade when you turn on features which require more HTML Elements and more calculations. I have pointed you out this fact in my previous post.
Best Regards,
Peter StoevjQWidgets Team
http://www.jqwidgets.com/Hi Peter
It should be expected that the performance would stay the same with the same data. I do expect, and am happy with performance degrading when I add more elements. Let me bulletpoint, and then you can point out where my understanding is flawed.
1. page size = 20. 14 rows are returned. a certain number of elements are generated and formatted
2. page size = 1000. 14 rows are returned. The same certain number of elements are generated and formatted because the same 14 rows are returned.
3. Autorowhight – This is a formatting thing that makes words wrap in cells. This is a default behaviour for DIV elements so switching it on will take a bit of time calculating the display height of the outer box switching it off will add more rows into the view… both points leading onto point 4
4. autoheight:true will size the outer container to the number of rows. In my case 14 regardless of the pagesize option, but dependent on the height of each row as in point 3. I understand that if I have 1000 actual rows coming back, 1000 rows will be displayed. Therefore, based on your previous comment, I agree, if I have a fixed height that only shows 10 rows at a time, and you are using some form of ‘windowing’ optimization a lot less elements will be generated in the ‘window’ at any one time.In summary, I have 14 rows coming back regardless of the chosen rows per page setting, so I don’t understand how it should be expected that when I have 14 rows coming back speed is ok, and then when I have 14 rows coming back speed is then unacceptably slow… its the same amount of data, with the exact same structure, and the exact same number of rows, in fact the exact same 14 rows, generating the same number of DIV elements in the DOM. The only thing changing is the grids ability to show more rows if they were to exist, since they don’t exist anywhere, not even in the database, it seems some optimisation should remove this overhead until such time as there are more rows coming back… back to me agreeing with you that 1000 rows of actual data would be unacceptably slow and I’m happy with that
I have managed to create working jsfiddles with my data to show my point.
http://jsfiddle.net/nigeljohnson73/cnsd2qnw/3/
Settings (right down the very bottom):autoheight: true, // height: 1000, autorowheight: true,
14 rows returned, 14 rows displayed, 20 rows per page – navigation slow, 1000 rows per page – navigation unusable
http://jsfiddle.net/nigeljohnson73/cnsd2qnw/4/
Settings (right down the very bottom):autoheight: false, height: 1000, autorowheight: true,
14 rows returned, 14 rows displayed, 20 rows per page – navigation ok, 1000 rows per page – navigation unusable
http://jsfiddle.net/nigeljohnson73/cnsd2qnw/5/
Settings (right down the very bottom):autoheight: false, height: 1000, autorowheight: false,
14 rows returned, 14 rows displayed, 20 rows per page – navigation good, 1000 rows per page – navigation good
kind regards
NigelHi Nigel,
I think you don’t understand my point. HTML Elements is something different than your Data Source. autoheight and autorowheight properties turned on means – jqxGrid will have Height equal to the Data Elements or Page Size and will have to automatically calculate the Height of each row based on its content. This requires the Grid to create HTML Element for each of your Data Element i.e many more HTML Elements than usual. If you do not believe me, check this with the DOM Explorer. Browsers become slower and slower when you have many HTML Elements on the same page. If you wish, follow my advice and turn off autoheight and autorowheight if you have to display 500+ rows on the same Grid page.
Best Regards,
Peter StoevjQWidgets Team
http://www.jqwidgets.com/Hi Peter,
I do get your point, but what ‘additional’ HTML elements are created for rows that don’t exist and don’t need to be rendered to the user?
With a page size of 1000, the height is still only big enough to show the 14 rows that exist. If you are creating elements for the other 986 rows that do not exist, this is a problem.
I have counted the DIV elements:
autorowheight disabled, autoheight disabled, 20 rows per page 3,714 DIV elements
autorowheight disabled, autoheight disabled, 1000 rows per page 3,714 DIV elementsInteresting, and understandable.
autorowheight enabled, autoheight enabled, 20 rows per page 1,618 DIV elements
autorowheight enabled, autoheight enabled, 1000 rows per page 19,258 DIV elementsI’m trying to understand why this is the case. What are the other 17,640 elements for? They are in no way useful since they don’t provide any more information to the user. These 17,640 elements should be optimised out until they are needed.
Kind regards
NigelHi Nigel,
Re-read my last post and probably you will understand. I do not have anything more to add here.
Best Regards,
Peter StoevjQWidgets Team
http://www.jqwidgets.com/Hi Peter,
Come on, I have played very nicely so far.
I’m trying to understand why a product (your product) I have paid a fair bit of money for is operating because it doesn’t make sense to me, and you are not being very helpful. This could be the language barrier and if so, I apologise, but I am asking for help understanding the process that goes on when the page size variable changes when the actual amount of data being displayed is not.
How about answering these questions:
1. Why are 17,640 extra HTML elements being created when they appear not to be needed?
2. If they are needed, what are they for?Kind regards
NigelHi Nigel,
autoheight requires the Grid’s height to be equal to the sum of rows count/page size count * rows height. Each Cell usually has several DOM elements. So we create HTML Element for each Cell displayed in your Grid when autoheight is turned on. autorowheight calculates the height of each cell and each row. autorowheight is not the default behavior as you think. If you have 20 rows per page with autoheight=true, the Grid requires a smaller amount of cells to be displayed. If you have 1000 rows per page with autoheight=true, the Grid requires larger amount of cells. This is very simple logic. If you have autoheight=false, the Grid will not have to create all the html elements. It will create the cells only for the view and when you scroll up/down it will reuse them. If your 980 additional rows does not provide information to the user, why do you set option for 1000 rows? If you display 1000 rows, expect more html elements on your page when autoheight is true.
Best Regards,
Peter StoevjQWidgets Team
http://www.jqwidgets.com/Hi Peter
This is my point. The number of actual data rows is variable. The data in the database can range from 0 to 100’s of rows per list. Why 1000? There isn’t an ‘all’ on the paginator so 1000 seemed like a good big number to show all the rows in nearly all use cases cases.
The same logic applies on an empty ‘new’ list with only a 10 rows/page setting. There are still a load of HMTL elements being created ‘just in case they may need to be used but actually never are’. It just seems that could be optimized out so performance can degrade based on the actual data shown to the user.
If local data needs to be added, the DOM could be updated with new elements at that point since the user would be expecting a slight delay.
Thanks for bearing with me
Kind regards
NigelHi Nigel,
Well, there is nothing that requires optimization in terms of HTML Elements creation in this widget. It creates the HTML Elements based on your settings and your data. We, however always investigate options for optimizations and will investigate this one, too. If you wish to learn how this widget works, please refer to its documentation and examples.
Best Regards,
Peter StoevjQWidgets Team
http://www.jqwidgets.com/ -
AuthorPosts
You must be logged in to reply to this topic.