#13112 closed defect (patchwelcome)
TabContainer: very slow tab changed, if content is big
Reported by: | Sergey Kravchenko | Owned by: | |
---|---|---|---|
Priority: | high | Milestone: | future |
Component: | Dijit | Version: | 1.6.1 |
Keywords: | Cc: | ||
Blocked By: | Blocking: |
Description (last modified by )
TabContainer is very slow tab changed, if content is big.
showcase:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <!-- Firebug profile: from Tab1 to Tab2 = (4467.993ms, 280806 calls) from Tab2 to Tab1 = (43.511ms, 808 calls) --> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="stylesheet" href="http://yandex.st/dojo/1.6.1/dojo/resources/dojo.css"/> <link rel="stylesheet" href="http://yandex.st/dojo/1.6.1/dijit/themes/claro/claro.css"/> <script type="text/javascript" src="http://yandex.st/dojo/1.6.1/dojo/dojo.xd.js.uncompressed.js"></script> </head> <body class="claro"> <div style="width: 350px; height: 290px"><div id="tc1-prog"></div></div> <script type="text/javascript"> dojo.require("dijit.layout.TabContainer"); dojo.require("dijit.layout.ContentPane"); dojo.addOnLoad(function() { var tc = new dijit.layout.TabContainer({ style: "height: 100%; width: 100%;" }, "tc1-prog"); var cp1 = new dijit.layout.ContentPane({ title: "Tab1", content: "from Tab1 to Tab2 = slow<br>from Tab2 to Tab1 = fast" }); tc.addChild(cp1); var cp2 = new dijit.layout.ContentPane({ title: "Tab2", content: "very big...." }); tc.addChild(cp2); //// big content... var ss = ''; for (var i = 0; i < 20000; i++) { ss += '<span class="dijit dijitReset dijitInline dijitToolbarText">label-' + i + '</span>'; } cp2.set("content", ss); //// tc.startup(); }); </script> </body> </html>
Attachments (1)
Change History (9)
comment:1 Changed 11 years ago by
Component: | General → Dijit |
---|---|
Description: | modified (diff) |
comment:2 Changed 11 years ago by
Reducing the number of calls to be corrected setting top-level node attribute "widgetId" (to simulate a widget). However, the total time is not appreciably diminished. Methods "_getPadExtents" and "_getMarginExtents" called each time you switch to the tab "Tab2" and executed a very long time.
new version showcase:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <!-- Firebug profile: from Tab1 to Tab2 = (2186.261ms, 827 calls): _getPadExtents 2 49.98% 1092.665ms _getMarginExtents 2 49.36% 1079.093ms from Tab2 to Tab1 = (56.874ms, 949 calls) --> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="stylesheet" href="http://yandex.st/dojo/1.6.1/dojo/resources/dojo.css"/> <link rel="stylesheet" href="http://yandex.st/dojo/1.6.1/dijit/themes/claro/claro.css"/> <script type="text/javascript" src="http://yandex.st/dojo/1.6.1/dojo/dojo.xd.js.uncompressed.js"></script> </head> <body class="claro"> <div style="width: 350px; height: 290px"><div id="tc1-prog"></div></div> <script type="text/javascript"> dojo.require("dijit.layout.TabContainer"); dojo.require("dijit.layout.ContentPane"); dojo.addOnLoad(function() { var tc = new dijit.layout.TabContainer({ style: "height: 100%; width: 100%;" }, "tc1-prog"); var cp1 = new dijit.layout.ContentPane({ title: "Tab1", content: "from Tab1 to Tab2 = slow<br>from Tab2 to Tab1 = fast" }); tc.addChild(cp1); var cp2 = new dijit.layout.ContentPane({ title: "Tab2", content: "very big...." }); tc.addChild(cp2); //// big content... var ss = '<div widgetId="__imitate_widget__">'; for (var i = 0; i < 20000; i++) { ss += '<span class="dijit dijitReset dijitInline dijitToolbarText">label-' + i + '</span>'; } ss += '</div>'; cp2.set("content", ss); //// tc.startup(); }); </script> </body> </html>
comment:3 Changed 11 years ago by
Milestone: | tbd → future |
---|
OK, well your results make sense.
When you switch tabs, ContentPaneResizeMixin is computing the content-box size of the new tab (this._contentBox), and that's taking a long time since the tab has so much data in it. Dijit is waiting for the browser.
I can defer the calculation of this._contentBox until/unless it's needed (and it isn't needed in this case), but it still takes a long time to switch tabs because TabContainer calls ContentPaneResizeMixin.resize(...) which needs to compute margins etc. in order to do the dojo.marginBox() call.
I can avoid that resize() call when we are simply sizing the pane to the same size it was before (since the TabContainer's size hasn't changed). However, then it still takes a long time, hanging in the ScrollingTabController._getScroll() method. Note that AFAICT _getScroll() isn't accessing anything to do with that big <div>.
Basically, at some point we end up doing a getComputedStyle() call that forces the browser to finish it's layout. So I think the delay is just inherent in having a <div> with so much data in it.
I'll leave this open for further consideration but I doubt there's anything dijit can do to make this faster.
Changed 11 years ago by
Attachment: | attemptedFixes.patch added |
---|
avoiding DOM computation but still slow
comment:4 Changed 11 years ago by
Your patch did not help much, as you promised.
My experiments have shown that a simple 'div' with nested 'div', each stack-like panel and a simple switch "display: none / display: block" solves my problem.
May need to simplify StackContainer? (at least optionally) for this behavior?
showcase:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="stylesheet" href="http://yandex.st/dojo/1.6.1/dojo/resources/dojo.css"/> <link rel="stylesheet" href="http://yandex.st/dojo/1.6.1/dijit/themes/claro/claro.css"/> <script type="text/javascript" src="http://yandex.st/dojo/1.6.1/dojo/dojo.xd.js.uncompressed.js"></script> </head> <body class="claro"> <div id="bb"> <div id="b1">show Tab1</div> <div id="b2">show Tab2</div> <div id="b3">grow size</div> </div> <div id="tp" style="width: 350px; height: 290px"> <div id="cp1" class="dijitTabContent" style="width:100%;height:100%"> Tab1 <div class="dijitTabContent">Tab1 nested</div> </div> <div id="cp2" class="dijitTabContent" style="display:none;width:100%;height:100%;"></div> </div> <script type="text/javascript"> dojo.require("dijit.layout.TabContainer"); dojo.require("dijit.layout.ContentPane"); dojo.ready(function() { var cp1 = new dijit.layout.ContentPane({ title: "Tab1" }, "cp1"); var cp2 = new dijit.layout.ContentPane({ title: "Tab2", content: "very big...." }, "cp2"); //// big content... var ss = '<div widgetId="__imitate_widget__">'; for (var i = 0; i < 20000; i++) { ss += '<span class="dijit dijitReset dijitInline dijitToolbarText">label-' + i + '</span>'; } ss += '</div>'; cp2.set("content", ss); //// var b1 = new dijit.form.Button({onClick:function() { dojo.style(cp2.domNode, "display", "none"); dojo.style(cp1.domNode, "display", "block"); }}, "b1"); var b2 = new dijit.form.Button({onClick:function() { dojo.style(cp1.domNode, "display", "none"); dojo.style(cp2.domNode, "display", "block"); }}, "b2"); var b3 = new dijit.form.Button({onClick:function() { dojo.style("tp", {width:"700px", height:"500px"}); }}, "b3"); }); </script> </body> </html>
comment:5 Changed 10 years ago by
Has there been any work on this? I'm writing a chat client in 1.7.2, and im having the exact same issue. The longer the chat content, the longer it takes to switch to the tab.
comment:6 follow-up: 7 Changed 10 years ago by
Resolution: | → patchwelcome |
---|---|
Status: | new → closed |
Nope sorry, AFAIK there's nothing that can be done.
comment:7 Changed 7 years ago by
Replying to bill:
Nope sorry, AFAIK there's nothing that can be done.
There is something, what can be done. Switching tab is slow, because it can have a (huge) content attached to DOM, even when Tab content is not visible. You can speed up things by temporarily removing not visible content of all not selected Tabs from DOM.
You can use tabContainer.watch("selectedChildWidget", function(property, notSelectedWidget, selectedWidget), then find all child nodes of notSelectedWidget by notSelectedWidget.getChildren() and use notSelectedWidget.removeChild(childNode) to temporarily remove it from DOM. Store childNode to notSelectedWidget.tempHiddenNodes[] = childNode.
Before removal a node from DOM, make sure it was fully initialized. You can postpone removing child nodes for some time (1 second), when it has sub widgets working with DOM (menus, etc) - to ensure a reference to DOM is available when tab was hidden before it was fully initialized. You may use tab.defer() method for deferred method run.
When notSelectedWidget became selectedWidget, use selectedWidget.addChild() for all selectedWidget.tempHiddenNodes to add hidden nodes to DOM. Do the same for notSelectedWidget, before notSelectedWidget is destroyed (use aspect.before for example), because some child Dijits can access DOM on destroy.
You can create new TabContainer? dijit implementing this and you will speed up whole Tab layout immediately. Just test is, so any sub widget has DOM available, when need it.
Would be nice to have it as an option directly in dijit/layout/TabContainer.
comment:8 Changed 7 years ago by
Hmm, when are you reattaching the content? You saw all the computations I listed in https://bugs.dojotoolkit.org/ticket/13112#comment:3. My guess is that in your benchmarks you delayed attaching the content until after those computations. But that will surely break something.
Hmm, the 280806 calls does sounds suspicious.