WinJS not unloading js/css
Asked Answered
M

4

7

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?

Mo answered 17/8, 2012 at 0:14 Comment(0)
E
13

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.

Ebullient answered 17/8, 2012 at 21:0 Comment(2)
The script files aren't such a problem, it's easy to name functions to not collide or just use WinJS.Namespace, css files however can be unloaded and it seems like it would be sensible. As it is, it seems everyone has just accepted that namespacing all your css classes is the way to go, which is really a total hack... At least it was deliberate but long term I'm sure it's one more thing MS is going to get criticism for.Mo
I'm not sure what the issue is besides having CSS files loaded when each page loads. The samples tell you to make sure you namespace your pages; it makes sense. You could probably use LESS/SCSS to help with namespacing, like with Web Essentials or Web Workbench.Berardo
C
3

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.

Chiccory answered 17/8, 2012 at 2:40 Comment(1)
I get the single page model, I just figured MS was a little smarter and unloaded resources of inner pages when you navigated away from them.Mo
I
0

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>
Instructor answered 17/8, 2012 at 8:1 Comment(1)
Yeah, I know I could do it that way, but that's exactly what I'm trying to avoid needing to hard code. I may just switch over to the multi-page implementation and not have any css for the individual parts, each master page will be a template, with all the css in a single file, then the sub pages loaded in won't have any of their own css.Mo
B
0

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;
        }
        }
});
Buckman answered 14/8, 2013 at 19:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.