I'm maintaining an ASP.NET site where users can log on to register some set of data (for statistical purposes). One user registers data for a set of units, and for each of these units a set of forms are to be filled out (with a handful of fields in each form, but that doesn't matter here). One scenario is that a user has 12 units, and in each of these units there is 25 forms to be filled, meaning a total of 300 forms.
The ASP.NET page for registering these data is made the following way: each form is in a panel that can be collapsed using an AjaxControlToolkit CollapsiblePanelExtender, and all forms in a unit is inside another panel that also can be collapsed. The result is that you have a tree view-like structure with the units on the top, and under each unit you can expand a set of forms, and further each form can be expanded to fill data (the page is loaded with all panels collapsed by default).
The page is generated completely dynamically (as forms can be added in a database), and for generating the CollapsiblePanelExtenders I have the following code:
private CollapsiblePanelExtender GenerateCollapsiblePanelExtender(string id, Panel headerPanel, Panel contentPanel)
{
CollapsiblePanelExtender collapsiblePanel = new CollapsiblePanelExtender();
collapsiblePanel.ID = id + ID_COLLAPSIBLE_PANEL_POSTFIX;
collapsiblePanel.TargetControlID = contentPanel.ID;
collapsiblePanel.CollapseControlID = headerPanel.ID;
collapsiblePanel.ExpandControlID = headerPanel.ID;
collapsiblePanel.Collapsed = true;
collapsiblePanel.BehaviorID = collapsiblePanel.ID + ID_BEHAVIOUR_POSTFIX;
return collapsiblePanel;
}
With one user having 12 units each with 25 forms, this means a total of 312 CollapsiblePanelExtenders. As I said, they are all set to be collapsed by default, but here's the problem:
When the page loads, they all appear to be expanded, and then the browser "starts collapsing them". This however takes a very long time (in Firefox I even get a warning about an unresponsive script, IE and Chrome only takes forever but without the warning). When all the "collapsing" is complete it works smooth to open and close single panels, but users have complained about the extremely slow initial loading.
So my question is simple: is there a way to optimize this so that the loading goes smoother? Is it for instance possible to only load the header panels in each CollapsiblePanelExtender initially, and then load the content panel asynchronously in some way?
One final clarification:
I know I could simply change the design of the page to only include one unit and thus reducing size of the contents drastically, but I hope to avoid this (users prefer the way with everything in one page). It would also mean a rather large change to the logic of the page (yes, I know - it's a poor code base at that point)
After asking some more around other places, I finally managed to solve this issue. The solution was to skip the CollapsiblePanelExtenders altogether, and instead use jQuery to handle the collapsing/extending.
In my structure, all header panels use the css class HeaderPanel, and all content panels use the css class ContentPanel (all of these are hidden by default). I can then use the following script to handle all the collapse/expand logic:
<script language="javascript">
$(document).ready(function() {
$("div.HeaderPanel").toggle(
function() {
$(this).next("div.ContentPanel").show("slow");
},
function() {
$(this).next("div.ContentPanel").hide("slow");
});
});
</script>
The solution was really quite simple, and it works like a charm! The collapsing/extending is soo much smoother and nicer than what it looked like when I used the CollapsiblePanelExtenders, and the page loads really fast as well :)
Related
I cannot seem to get Chrome to pop up an "alert" page. The alert page has code in it, so it can't really be a DIV or I would just do it that way. It worked for many years, but likely do to a Chrome update it will no longer function. Still works fine in IE11, though.
The following code is used to pop up an "alert" page when there is an alert that is queried from a Database. It has always worked until recently (15 years and running)
CODE:
ClientScript.RegisterStartupScript(GetType(Page), "Alarm", "<script language='javascript'>window.showModalDialog('Alarm.aspx?ID=" & AlarmID & "', null, 'dialogWidth=460px;dialogHeight=310px;status=no;resizable=yes');document.frmA.submit();</script>")
I've tried a few things like windows.open and creating a hidden button on the asp.net page and then using the click event. Nothing works. I do not see a blocked popup in Chrome and I have even went into settings and did the following:
Set Safe Browsing to "No Protection"
Set allow pop-ups and redirects on the server name (http://servername and http://localhost)
As noted, near all browsers quite much have clamped down on popup windows. this makes things more difficult for web developers.
There are two good approaches. one I don't fancy at all is using bootstrap dialogs, but they tend to "sort of work all on their own" kind of deal based on class settings for divs etc. - really hard to debug.
Since near all sites these days include jQuery for your js code, then I quite much hands down recommend you introduce jquery.UI. It has a whole slew of nice things such as date pickers etc. But it also has a rather nice dialog pop option. They just work, and when you code them up? They follow "normal" like code approaches.
it not quite clear if your message/dialog pops after say a button click (and post back), and the at the end of that process, you need/want some dialog message to display. But all in all, I would high recommend jQuery.UI for this dialog/message that you need.
jQuery.UI in most cases expects the content you want to "display/pop" exists in a simple div in the current existing page. However, it also works VERY well if you supply the dialog another existing web page. The only REAL big issue to keep in mind? That dialog page you pop cannot handle multiple post-backs. (so, some buttons, or ONE post back in that dialog is fine - but you ONLY get the ONE post-back.
So, if that page display allows some input, or some interaction and ONLY requires ONE post-back, then jQuery.UI is again great. If that pop page requires several buttons and several post-backs, then you are in for a world of pain and hurt - jQuery.UI dialogs (like most) cannot survive or handle multiple postbacks. Any post-back means the dialog closes (collapses). So in those cases, you have to adopt ajax calls (web methods) if you need/have/want that page to have more then one active post-back button or event.
So, you could have/place a script in even your master page, and little function code stub that your register script can call.
Or, I suppose you could inject the whole script, but the script would look like this:
So, the pop page actualy is SHOVED into a div. So we have a div that "holds" the page.
The jQuery.UI code script then looks like this:
<div id="poppagearea">
</div>
<script>
function showpage() {
var mydiv = $('#poppagearea');
mydiv.dialog({
autoOpen: false, modal: true, title: 'My cool other page', width: '30%',
position: { my: 'top', at: 'top+150' },
buttons: {
'ok': function () {
mydiv.dialog('close');
alert('user click ok');
},
'cancel': function () {
mydiv.dialog('close');
alert('user click cancel');
}
}
});
mydiv.load('Default.aspx');
// Open the dialog
mydiv.dialog('open');
}
So, in above, we loaded "default.aspx" into that dialog and thus displayed it on the page.
So, I would consider jQuery.UI - but it does mean adopting a new js library into your existing project.
The pop page does gray out the full page, and you do get a title bar, and your own ok, cancel button. The above thus looks like this:
So, it does a great job - but as noted, that page can only have one post-back, and it can't be a general working aspx page with lots of buttons and post backs - but it will render and display rather well.
I have to add a progress bar on my web page to show the progress of submission. My page on submit saves the data in the database. I have to show user how long will the submission take place. For an e.g., 50% Completed and so on..
in order to show a progress for the update process, you will need to send partial updates indicating the total percentage completed so far.
so the more applicable solution will be to use something like jQuery UI ProgressBar, and have a mechanism to send the mentioned percent to the user, then update the progress bar with the last sent value.
you can check the following articles for more details:
Using the jQuery ProgressBar Widget in ASP.NET Applications
Reporting server side operation progress with jQuery UI Progressbar
I see two ways in here, first, when you are going to do it asynchonous request, you can measure how long this takes in an average (10secs) and use this value for measure how far are we now.
Second way is more suitable for huge data uploads .. You would need the database and application support and upload the data using batches. Then you can measure how many data you have and how far are you in the submitting.
You can simulate it with jQuery and a timer that extends the length of a colored div. Not really an server side genuine control but can be enough to make the visitors satisfied. The time and interval can be calculated due to what the user have to do.
Often, for the user experience, the look of the progress bar movement itself, it's more important then the fact its going to 100% (which usually are bad, if the action isn't fully finished before 100%).
[added]
You have to
Attach jquery script library with
<script src="http://code.jquery.com/jquery-1.5.1.min.js" type="text/javascript"></script>
Place and point a styled html control
<div style="height:20px;width:200px;background-color:#777" id="somediv"></div>
Point to it and affect it with javascript/jquery
<script type="text/javascript">
var width = 10;
$(document).ready(function() {
setTimeout("$('somediv').html('width,'" + width+10 + "')", 600);
});
</script>
Please understand this is a sort of pseudocode (not fully copy pastable) to get an idea of my description. The setTimeout javascript function would make the div being bigger for each interval. Like adding 10px each, which make a good point to reuse the variable for a textaul description.
I'm developing a grid with excel-like functionality using the telerik controls. For example users can click on a cell and the cell turns into an input control for editing. Users can tab to move onto the next cell or use arrow keys for up/down to get the cell above or below. I've found the standard telerik grid is good but I've had to extend it with custom javascript to achieve what I need.
My problem is performance in the IE browser. While Firefox, Chrome, Safari are all fine, IE is a real pain. IE8 is considerably better than IE7 however moving around with the cursor keys is a bit unnatural, and nothing like as smooth as Chrome or FF.
I can't really post sample code due to the complexity of what the grid is doing, but generally I'm displaying the standard telerik grid and using the telerik javascript API to fill and bind in the browser. When a cell is clicked a javascript function moves a previously hidden input control into the cell from a hidden collection and gives it focus. When you tab away the cell value is cleared and the server is updated using ajax pagemethods and the next cell is selected in a similar manner.
The grid has approx 40 columns and 20 rows, i.e. 800 extra controls are hidden on the page and only activated by clicking a cell or through navigating with the keyboard. I originally had just one hidden control for each column but moving up and down with the cursor keys became problematic in IE.
Any advice for things to check that might speed up IE8 would be greatly appreciated.
//selects a cell and sets the value
this.select = function(value) {
this.moveFromTo(this._hiddenCell, this._gridCell);
this._bIsSelected = true;
this.set_inputValue(value);
this._focus();
}
//clears inner content for a cell
this.removeChildrenFromNode = function(node) {
if (node == undefined || node == null) {
return;
}
var len = node.childNodes.length;
while (node.hasChildNodes()) {
node.removeChild(node.firstChild);
}
}
//move back or forwards between hidden or active cell
this.moveFromTo = function(from, to) {
var currChild = null;
this.removeChildrenFromNode(to);
var i = 0;
if (from.childNodes != null) {
while (i < from.childNodes.length) {
currChild = from.childNodes[i];
if (to != null) to.appendChild(currChild);
i += 1;
}
}
this.removeChildrenFromNode(from);
}
Load up your page in IE8, open the developer toolbar F12 and turn on the profiling:
Profiler (tab) > Start Profiling
Use your grid for a bit as normal, and let IE profile your code.
When done, click Stop Profiling, and verify which function calls are chewing up the memory or taking the most time.
They may be ones that are beyond your control (e.g. in Telerik's code) but if anything you've added is the bottleneck post the function(s) back here on SO to ask for advise on how to optimize.
It sounds as though most if not all of your controls related to the grid are created from within JavaScript?
If so there are a couple of things to keep in mind:
IE hates string concatenation: there are numerous posts about it's poor performance
Ensure your clearing your events when switching controls and not just overwriting them
memory leaks are not your friend
IE hates adding controls as much as you do - so reuse them when possible
IE is faster if the controls are created via HTML (why oh why?)
IE hates it when you add lots of dynamic images and CSS with on-the-fly HTML controls
IE prefers innerHTML to addChild() (seems counter intuitive to string issue above)
etc
etc
There's many more, but with IE you also have to implement almost every single JavaScript performance suggestion you can find:
short variable names
ensure variables are properly scoped (otherwise the runtime will jump up scopes until nothing is left to search)
iterators from frameworks like prototype and jQuery are often slower than traditional for and while loops (VERY VERY sad but quite true)
etc
etc
When a cell is clicked a javascript
function moves a previously hidden
input control into the cell from a
hidden collection and gives it focus.
You have to explain the quote above in more detail. How exactly do you move the previously hidden control into the cell? Check the site below for a benchmark that uses different methods to generate a dynamic table. Moving the control using the W3C DOM methods or table methods could slow down IE while working fine in other browsers.
http://www.quirksmode.org/dom/innerhtml.html
Edit: Try this to check if it's faster(not as a final solution):
this.moveFromTo = function(from, to) {
to.innerHTML = from.innerHTML; }
Here are some useful links
Understanding and Solving Internet Explorer Leak Patterns
IE Sieve, Memory Leak detector for Internet Explorer
JavaScript Profiling
Try Google Frame. It pushes the performance on IE8 ;)
Test you code with jslint.com
I dynamically add rows to divStaff using jquery:
$("span[id$='lblAddStaff']").click(function() {
//$(".staff_tpl").find("input[id$='txtRate']").val("0,00");
var staff_row = $(".staff_tpl");
staff_row.find(".staff_row").attr("id", "Emp" + counter);
$("div[id$='divStaff']").append(staff_row.html());
counter += 1;
});
the row that I'm adding is inside the hidden div with class=".staff_tpl"
I append the contents of this div to divStaff
When I submit the page (postback), the resulting divStaff is always empty if I try to display it like this:
lblTest.Text = divStaff.innerHtml.ToString
basically, I'm manipulating a div client side, and I want to access it server side via the code-behind of my aspx page. I think I'm missing a basic principle here.
This cannot be done.
If you want to access data you've created pn the page, you have to place it inside input fields (possibly hidden), and access it after it was posted using Request.Form["MyHiddenFieldName"].
<div>s aren't posted to the server. runat="server" elements are enechoded in the ViewState (a big string, really - you can see it in the source of your page), giving the abstraction of continuity (or the illusion of it). However, that sting isn't aware of changes you make in the DOM.
When dealing with runat="server" elements, you will see the last changes you've made on the server side, but client side changes are gone.
Only <input> (and text area, option, etc) values are posted to the server on submit, so changing these on the client will be seen on the server, after the page was posted.
I have rather a complex UI. However, for the purpose of this question, let's say that there is a HTML table that renders UILayout1 by default (say default mode). There is a button that a user can use to toggle between the default mode and a preview mode (UILayout2)
When in preview mode, there are some columns in the table that are invisible and there are reordering of rows. I am using JS (jquery) on load to check the mode and change it accordingly.
The table and the toggle button are in UpdatePanels.
Functionally, everything works as expected. However, when a user toggles between default and preview mode or vice versa, there is this short time interval in which the the table renders in default and then JS runs to make changes.
This results in degraded UI experience. Are there any creative ways to avoid this "flicker"?
you can use DIVs or don't use update panel in your UI generation use any concept else
The problem is likely to be that your code is running on load. I'm assuming that you're doing this using the standard jQuery method of running code on load, and not using the window's onload event. In any case, even using jQuerys $(document).ready(...) will be too slow if you have a lot of other javascript files to load, as the .ready event isn't fired on the document until all javascript includes have loaded.
You should be able to work around the issue by including your code that modifies the table just after the html for the table in your page and not running it on load i.e. make sure you don't wrap it in $(document).ready(...);
For this approach to work, you will need to have all javascript required by the code which is modifying the table included earlier in the page.
If you have other non-essential javascript files included, you should try to include them later in the page.
I'm not 100% sure how being inside an update panel will affect it - you will need to make sure that your code is being re-triggered when the updatepanel updates, but I believe this should all happen automatically.
Presumably your UI is controlled by CSS? You might be able to get rid of the flickering by adding something like this at the start of your JavaScript or in the <head> of your HTML:
if (previewMode) {
document.documentElement.className = 'preview';
}
Then if you modify your CSS rules that apply to your preview mode to reflect the HTML element having the class="preview" to something like:
.preview table .defaultMode {
display:none;
}
hopefully your table should render correctly first time and will not need to be re-drawn.