WinJS not unloading js/css - windows-8

I'm working on a Windows 8 Metro app and I've found (even on their sample applications where I haven't touched the code) that as you navigate between pages, the top level "default.html" acquires every single js and css file ever loaded during the application's run.
This is causing me a lot of headaches as my css is colliding between difference pages. Am I missing something or is this is serious bug?

Not unloading JavaScript and CSS was a deliberate choice, not an accident or oversight.
First off, understand that page controls are purely a JavaScript construction - the browser engine has absolutely no knowledge of them. The browser just sees a chunk of DOM that was dynamically generated by scripts.
The web platform doesn't let you unload script files - once they're loaded into the context, they're there forever.
With CSS, they could have tried removing tags, but it opens up a can of worms. Depending on which order pages are navigated to, you could end up with different styles applied in the same app. What if two pages refer to the same style sheet? Do you add the same link tag twice? And which one do you remove?
It's a mess. Instead, WinJS guarantees that scripts and stylesheets will be loaded once and only once, the first time they're referenced. So you can have every page in your app reference "myStyles.css" and it'll only be loaded once (and there will only be one style tag).
So what do you do to prevent the issues you're seeing? First off, remember you're building an app, not a web site that will arbitrarily grow new content. Decide on your general styles and classes. Put shared styling in your default.css and reference it from your default.html file.
For individual pages, the easiest thing to do is prefix your styles with the page name. Instead of:
<div class='intro'></div>
do
<div class='page1-intro'></div>
Then you're guaranteed to avoid collisions.
If you're referencing page elements by ID, well don't do that. Using ID's in pages causes all sorts of potential weirdness (what if you render the same page control twice at the same time? Also, the ID doesn't exist until after the page has been loaded into the DOM, which means data-win-options references by ID don't work). But if you insist, again, consider prefixing the ids with the page.
Basically, set up ad-hoc namespaces to keep you from colliding. It's a lot easier than ripping out link tags manually and will result in a lot better app experience than doing full navigations.

Its not a bug, it is part of the default app pattern used by the WinJS tempaltes. The default WinJS templates use a single-page model, meaning that all content is loaded into the default.html using a PageNavigatorControl. As a result, there is a single DOM in memory at all time. If you followed a similar pattern in a regular browser, you would see the same behavior.
You can, if you want, use more traditional navigation using multiple pages and traditional href links. That is not the recommended approach, but if you are trying to bring existing web assets built using that model, it can make things easier.

You can resolve this problem by querying the document for the link elements that import your styles and disabling the ones you don't want. You need to make sure that you don't disable the MS CSS files and the default.css file in your project, assuming you use it to define the common styles for your app.
Here is a sample that shows you how to do it. This is a file called page2.html which, when loaded by the WinJS.Pages.UI.render method will locate and disable the link elements it doesn't want. It makes sure that the page2.css file is enabled and keeps a list of the files it simply ignores.
(I put this in the ready handler function, but I tend to use this technique in the handler for the WinJS.Navigation events and rely on consistent file naming to get the result I want).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>UnloadCSS</title>
<!-- WinJS references -->
<link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>
<!-- UnloadCSS references -->
<link href="/css/page2.css" rel="stylesheet" />
<script>
WinJS.UI.Pages.define("/page2.html", {
ready: function () {
var ignoreList = ["/css/ui-dark.css", "/css/ui-light.css", "/css/default.css"];
var myCSS = "/css/page2.css";
WinJS.Utilities.query("link").forEach(function (linkElem) {
if (linkElem.href.indexOf(myCSS) > -1) {
linkElem.disabled = false;
} else {
var ignore = false;
ignoreList.forEach(function (ignoreItem) {
if (linkElem.href.indexOf(ignoreItem) > -1) {
ignore = true;
}
});
if (!ignore) {
linkElem.disabled = true;
}
}
});
}
});
</script>
</head>
<body>
<button>Change </button>
</body>
</html>

this could be a good solution with a convention names aproach :
var currentPage = Application.navigator.pageControl.uri.replace("ms-appx://" + Windows.ApplicationModel.Package.current.id.name.toLowerCase(), "");
var currentCss = currentPage.replace(".html", ".css");
var ignoreList = ["/css/ui-dark.css", "/css/ui-light.css", "/css/default.css"];
WinJS.Utilities.query("link").forEach(function (linkElem) {
if (linkElem.href.toLowerCase().indexOf(currentCss) > -1) {
linkElem.disabled = false;
} else {
var ignore = false;
ignoreList.forEach(function (ignoreItem) {
if (linkElem.href.toLowerCase().indexOf(ignoreItem.toLowerCase()) > -1) {
ignore = true;
}});
if (!ignore) {
linkElem.disabled = true;
}
}
});

Related

Vue.js 3 unmount and memory leak

For purpose of this post i created a simple example:
http://wagoon.demoeshop.net/test-remove-vue.html
In this example you will find two buttons.
First button creates DIV element, then creates a Vue app and mount it to that div
Second button will unmout the app
Example code
In my example, you will find two buttons
<button type="button" onclick="myTest.mount()">.mount()</button>
<button type="button" onclick="myTest.unmount()">.unmount()</button>
Vue.js 3 is included
<script src="https://unpkg.com/vue#next"></script>
Whole javascript code is wrapped in function testClass() for debugging reasons:
function testClass(){
// vueApp is public just for debugging reasons
this.vueApp = null;
// creates DIV with id #appDiv and apends it to <body>
function createDiv(){
var div = document.createElement('div');
div.id = "appDiv";
document.body.append(div);
}
// creates new Vue app and mounts it to #appDiv
this.mount = function(){
createDiv();
this.vueApp = Vue.createApp({"template":"Vue mounted"});
this.vueApp.mount('#appDiv');
}
// unmounts Vue app
this.unmount = function(){
// MEMORY LEAK HERE:
this.vueApp.unmount('#appDiv'); // this line should mark vueApp as collectable for garbage collector,it's not
this.vueApp = null; // event this line does not help
// SOLUTION: only removing app taget element from DOM is marking object created with Vue.createApp()
// as collectable by garbage collector.
// document.querySelector('#appDiv').remove();
}
}
myTest = new testClass();
How to find memory leak in google chrome console:
For debugging reason, created app is stored to this.vueApp in testClass so we can find the object id easily. Just follow these steps
Run the code
click on first button (.mount Vue app). "Vue mounted" text will appear
open chrome console and switch to Memory tab
Take a heap snapshot
click on second button (.unmount Vue app). "Vue mounted" text will disappear
back on the Memory tab click on "Collect garbage" (icon with dustbin)
Take second heap snapshot
Switch to first taken snapshot and filter "testClass". (you will see only one result). Open it and find public property "vueApp". Next to it you will find #ID of object stored in this property (for example #567005)
Switch to second snapshot and press CTRL+F (find). Search for the same #ID (for example #567005). Here is memory leak: object created with Vue.createApp is still in memory! It was NOT collected with garbage collector, because something is still pointing to this object
How to solve this memory leak
Only solution I found is removing DIV#appDiv from the DOM (code for removing this element is commented in the myTest.unmount() method). After that, calling garbage collector again will remove this object from memory.
Is there any other solution?
Why is this (big) problem
In big apps with multiple screens, creating and deleting whole app is the only way, how to save memory (script just loads code for actual page, and when user wants another page, actual page is destroyed and new page is loaded, then new Vue app is created)
You can't also solve this problem with creating dynamic components, because Vue3 removed (and i thing its big mistake) the $destroy method, so when you create new component for new screen, the old component will remain in memory forever.
Vue router will not solve this problem, because Vue router loads all pages on start and that is not acceptable in big apps, because the network bandwidth will be huge (megabytes of code loaded for just one app is just wrong)
Fixed in VUE 3.0.6
VUE js version 3.0.6 fixed this problem

jQuery innerHTML cannot set property

I have a <div id="tguide"> that I use as a place holder. I was told iframes are inferior to jquery load. So this is what I have:`$(window).on('load resize',function(e){
var transferguidelocation = "../"
var title = $(document).attr('title');
if(title == 'System Manuals') {
var transferguidelocation = "../SystemManuals/"
};
document.getElementById("tguide").innerHTML='<object type="text/html" data="' + transferguidelocation + 'shared/TransferGuide.html" style="width:100%;height:100%;"></object>';
As you can first it checks to see which page it's on, then it adds an extra folder location to the TransferGuide.html location.
The file loads into the <div> no problem, it looks exactly the way I want it to and data is correct, the problem is that I'm getting the below error in the browser inspector:
It's a bit annoying and I'm not a fan of the errors. Does anyone know how I can get rid of this?
I figured it out. I was referencing my custom.js file before the js.min file, so it didn't recognize it. I just reversed the two and the error went away

DOMPDF: page_script(): Check current page content?

I am using dompdf to generate reports which sometimes has a front page, and sometimes has some attachments. The main content has a header and a footer, but the front page and the attachments (the last 3 to 5 pages of the pdf) should not contain header and footer. I'm placing the header and footer with a inline php page_script(), like this:
<script type="text/php">
if (isset($pdf) ) {
$pdf->page_script('
if ( $PAGE_COUNT > 10 && $PAGE_NUM == 1) {
//front page footer
}else{
//other pages' header and footer
}
');
}
</script>
The whole report is built by a database engine which outputs it all as a temporary html.txt-file which is then read into DOMPDF.
Now, to recognize the front page I just have to check if the page number is = 1. If any attachments are added (which are comprised of 1000px-height jpg images, each on their own page)
Does anyone have an idea for how to identify these "attachment"-pages and get DOMPDF to not render a header and footer on those pages? Or is there any way I could check within the page_script()-script whether the current page contains only an image (or perhaps an image with a specific class or identifier)?
Thanks for any help,
David
Detecting the current page contents may be possible, but I'd have to research exactly what you can do at this level. An easier method would be if you could inject some inline script in your generated document. After the main content and before the attachments you could add something like $GLOBALS['attachments'] = true; and then add a check on the status of this variable to your conditional.
<script type="text/php">
if (isset($pdf) ) {
$pdf->page_script('
if ( $PAGE_COUNT > 10 && $PAGE_NUM == 1) {
//front page footer
}elseif ($GLOBALS['attachments']) {
//attachments actions
}else{
//other pages' header and footer
}
');
}
</script>
Of course, don't forget to initialize the variable to false at the top of the document.
(from https://groups.google.com/d/topic/dompdf/mDsYi8Efnhc/discussion)

Dynamic Height Adjusting w/ Open Social Gadget

I have a gadget that is a glossary with a number of different pages. users can upload new words to the data source and those words will be pulled into the glossary through an AJAX call.
I want to resize the gadget window everytime the window is re-sized OR a new letter is selected and the page height changes (ie the gadget html block height).
Google developers has posted this on their website. However, this clearly is not working for me. The scrolling is not registering on the iframe and the height is not adjusting when the window is resized.
Here are my ModulePrefs
title="Climate Policy and Science Glossary"
description="Paragraph format"
height="300"
scrolling="true">
<Require feature="dynamic-height"/>
<Require feature="opensocial-0.8" />
Here is the gadget's script telling it to adjust:
window.onresize = adjust;
function adjust() {
var wndwH = gadgets.window.getViewportDimensions().height,
wgtH = $('#_glossary').closest('html').height,
h = Math.min(wndwH, wgtH);
gadgets.window.adjustHeight(h);
}
gadgets.util.registerOnLoadHandler(adjust);
What's going on? Am I doing something wrong or is there anyone else out there having trouble with Google's dynamic height??
The adjust function really only needs:
function adjust() {
gadgets.window.adjustHeight();
}
That should fit everything automatically.

Adding a dijit.Editor dynamical onClick

I want to add dijit.Editor after clicking on element.
dojo.ready( function() {
var handle = dojo.connect(dojo.byId("k"),"onclick",
function(){
dojo.byId("k").innerHTML +=
"<div data-dojo-type=\"dijit.Editor\" id=\"editor\" style=\"width:400px;min-height:100px;background-color:white;border-style:solid;border-width:1px\" > </div>";
dojo.disconnect(handle);
}
);
});
In general this code works but dijit.Editor is malformed, one cannot write in it and there are no default plugins. How can I fix this?
Is this for inline editing ? Because if so, you should follow the examples on this page.
Othewise, you should do something like this rather than creating html in javascript.
I believe inlineEditBox is better fit to your need.
Also more generally, you add declarative code, in an onClick, after dojo.ready.
Basically it means that the html is "probably" parsed, then you get dojo.ready, do your connect, when the onClick happens, you add declarative markup in your html code, but you are not calling the parser.
I would recommand using programmatic approach for these kind of cases and keep declarative for templated widgets, or html string you will "parse" calling the parser.
Hope this also helps a little ;)
<script>function(ready, Editor, AlwaysShowToolbar, dom, query){
this.createEditor = function(){
new Editor({
height: '40px',},dom.byId('programmatic2'));autosave:false,
query('#create2').orphan();
}
});
</script>
try this.. u might get a solution..