javascriptknockout.js

Knockout.js - simple "Hello World" fails


I am doing a very simple hello world for knockout js : but my code is generating an error I don't understand.

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>AJAX Example</title>
    <script src="../Scripts/jquery-2.0.3.js"></script>
    <script src="../Scripts/knockout-3.0.0.debug.js"></script>

    <script>

        // Here's my data model
        var ViewModel = function (first, last) {
            this.firstName = ko.observable(first);
            this.lastName = ko.observable(last);

            this.fullName = ko.computed(function () {
                // Knockout tracks dependencies automatically. It knows that fullName depends on firstName and lastName, because these get called when evaluating fullName.
                return this.firstName() + " " + this.lastName();
            }, this);
        };

        ko.applyBindings(new ViewModel("Planet", "Earth")); // This makes Knockout get to work

    </script>
</head>
<body>

    <p>First name: <input data-bind="value: firstName" /></p>
    <p>Last name: <input data-bind="value: lastName" /></p>
    <h2>Hello, <span data-bind="text: fullName"> </span>!</h2>

</body>
</html>

the ko.applyBindings call raises the error:

Uncaught TypeError: Cannot read property 'nodeType' of null knockout-3.0.0.debug.js:2439

from the knockout-3.0.0.debug.js code:

// Perf optimisation: Apply bindings only if...
// (1) We need to store the binding context on this node (because it may differ from the DOM parent node's binding context)
//     Note that we can't store binding contexts on non-elements (e.g., text nodes), as IE doesn't allow expando properties for those
// (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)
var isElement = (nodeVerified.nodeType === 1);

I'm too ignorant to know what I'm doing incorrectly...


Solution

  • 2 ways to solve it i guess.

    1 easiest way: wrap your script inside

    $(document).ready(function() {
        your script goes here
    });
    

    use jQuery's ready() function to delay the initialization until the page has loaded.

    2 move your script under:

    <p>First name: <input data-bind="value: firstName" /></p>
    <p>Last name: <input data-bind="value: lastName" /></p>
    <h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
    

    HTML is parsed top-to-bottom. if you put the scripts before html elements, they could be run before some or all page elements are ready to be interacted with.