rymate1234
rymate1234

Reputation: 448

Windows Store Cordova App + WinJS: app crashes with a JavaScript runtime error on launch

I'm attempting to learn how to use WinJS in a Cordova app in Visual Studio 2015. I'm currently trying to get a basic listview working as described on the Try WinJS website. The app launches successfully on my android phone, however on my Windows 8.1 PC the app refuses to launch past the splash screen, giving me the error 0x800a138f - JavaScript runtime error: Unable to get property 'firstElementChild' of undefined or null reference

My current index.html is the following:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>WinJSTest</title>

    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
    <meta name="msapplication-tap-highlight" content="no" />

    <!-- WinJS references -->
    <link href="css/ui-dark.css" rel="stylesheet" />
    <script src="js/winstore-jscompat.js" ></script>

    <!-- WinJSTest CSS references -->
    <link href="css/index.css" rel="stylesheet" />
</head>
<body>
    <!-- Simple template for the ListView instantiation  -->
    <div class="smallListIconTextTemplate" data-win-control="WinJS.Binding.Template" style="display: none">
        <div class="smallListIconTextItem">
            <div class="smallListIconTextItem-Detail">
                <h4 data-win-bind="textContent: title"></h4>
                <h6 data-win-bind="textContent: text"></h6>
            </div>
        </div>
    </div>

    <!-- The declarative markup necesary for ListView instantiation -->
    <!-- Call WinJS.UI.processAll() in your initialization code -->
    <div id="listView"
         class="win-selectionstylefilled"
         data-win-control="WinJS.UI.ListView"
         data-win-options="{
            itemDataSource: Sample.ListView.data.dataSource,
            itemTemplate: select('.smallListIconTextTemplate'),
            selectionMode: 'single',
            tapBehavior: 'none',
            swipeBehavior: 'none',
            layout: { type: WinJS.UI.ListLayout }
    }">
    </div>

    <script src="js/WinJS.js"></script>

    <!-- Cordova reference, this is added to your app when it's built. -->
    <script src="cordova.js"></script>
    <script src="scripts/platformOverrides.js"></script>

    <script src="scripts/index.js"></script>
</body>
</html>

The index.js can also be found below:

// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkID=397704
// To debug code on page load in Ripple or on Android devices/emulators: launch your app, set breakpoints, 
// and then run "window.location.reload()" in the JavaScript Console.
(function () {
    "use strict";

    document.addEventListener( 'deviceready', onDeviceReady.bind( this ), false );

    function onDeviceReady() {
        // Handle the Cordova pause and resume events
        document.addEventListener( 'pause', onPause.bind( this ), false );
        document.addEventListener( 'resume', onResume.bind( this ), false );

        // TODO: Cordova has been loaded. Perform any initialization that requires Cordova here.
        var items = [];

        // Generate 2000 items
        for (var i = 0; i < 2000; i++) {
            items.push({ title: "Lorem Ipsum " + i, text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s " });
        }

        var itemList = new WinJS.Binding.List(items);

        WinJS.Namespace.define("Sample.ListView", {
            data: itemList
        });

        WinJS.UI.processAll();

    };

    function onPause() {
        // TODO: This application has been suspended. Save application state here.
    };

    function onResume() {
        // TODO: This application has been reactivated. Restore application state here.
    };

} )();

Hopefully someone with more experience can figure out what I'm doing wrong :D

Upvotes: 0

Views: 1839

Answers (3)

tsdude
tsdude

Reputation: 187

put this in your startup code somewhere. a bit funky, but works...

  1. Code will append to the element.innerHTML property setter to always remove head/body afterwards.
  2. Code will append to the WinJS.UI.processAll to always clean all head/body's afterwards.
  3. Most of this code is stolen from an earlier poster (HeTh)
  4. This should only be used for windows store apps.

    (function () {
    var winjs_drop_head_body = function (elem) {
    if (!elem.winControl)
        return;
    
        //var before = elem.innerHTML;
        var head = elem.firstElementChild;
        if (head.localName == 'head') {
            var body = head.nextElementSibling;
            while (body.firstElementChild) {
                elem.insertBefore(body.firstElementChild, head);
            }
            $(head).remove();
            $(body).remove();
        }
        //var after = elem.innerHTML;
    };
    
    var winjs_drop_head_body_all = function () {
        var heads = document.querySelectorAll('head');
        for (var h = 0; h < heads.length; h++) {
            var head = heads[h];
            if (head.parentNode.localName != 'html') {
                winjs_drop_head_body(<any>head.parentNode);
            }
        }
    };
    
    // drop the head after setting innerHTML
    WinJS.Utilities.ready(() => {
        var property = "innerHTML";
        var propertyDescriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, property);
        var getter = propertyDescriptor.get;
        var setter = propertyDescriptor.set;
        Object.defineProperty(HTMLElement.prototype, property, {
            get: getter,
            set: function (value) {
                var that = this;
                setter.call(that,value);
                winjs_drop_head_body(that);
            },
            enumerable: propertyDescriptor.enumerable,
            configurable: propertyDescriptor.configurable,
        });
    });
    
    var oldApplyAll = WinJS.UI.processAll;
    WinJS.UI.processAll = function (rootElement?, skipRoot?) {
        var oldVal = oldApplyAll(rootElement, skipRoot);
        oldVal.done(() => {
            winjs_drop_head_body_all();
        });
        return oldVal;
    }
    

    })();

Upvotes: 1

HeTh
HeTh

Reputation: 1

I had the same problem. I found that setting innerHTML did not work as expected. The contents was always set as <head></head><body>CONTENTS</body> if contents contains markup.

Even if you set it manually in QuickWatch in the Debugger.

I think it is related to security constraints in store apps.

I have written a function to delete the head and body elements:

export function winjs_drop_head_body_all() {
        var heads = document.querySelectorAll('head');
        for (var h = 0; h < heads.length; h++) {
            var head = heads[h];
            if (head.parentNode.localName != 'html') {
                HMSMapUtil.winjs_drop_head_body(head.parentNode);
            }
        }
    }
    export function winjs_drop_head_body(elem) {
        var head = elem.firstElementChild
        if (head.localName == 'head') {
            var body = head.nextElementSibling;
            while (body.firstElementChild) {                
                elem.insertBefore(body.firstElementChild, head);
            }
        }
    }

I have called winjs_drop_head_body_all() at the end of WinJS.UI.processAll().done(...) and winjs_drop_head_body(elem) after each line with .innerHTML = in winjs.js.

This is rather a hotfix, not a real solution, but I have found no other way yet.

Hope someone can find a better solution.

Upvotes: 0

Dave Voyles
Dave Voyles

Reputation: 4605

I would check to see what the FirElement is, and whether or not it is null.

From there, you can try to find what the first child is. I use console.log like a crazy person when starting off.

So in this case, I'd start the app with:

console.log(object)

then

console.log(object.firstElementChild)

This thread explains it in a bit more detail as well: Unable to get property 'options' of undefined or null reference

Upvotes: 1

Related Questions