101

I have this knockout code:

function Task(data) {
    this.title = ko.observable(data.title);
    this.isDone = ko.observable(data.isDone);
}

function TaskListViewModel() {
    // Data
    var self = this;
    self.tasks = ko.observableArray([]);
    self.newTaskText = ko.observable();
    self.incompleteTasks = ko.computed(function() {
        return ko.utils.arrayFilter(self.tasks(), function(task) { return !task.isDone() });
    });

    // Operations
    self.addTask = function() {
        self.tasks.push(new Task({ title: this.newTaskText() }));
        self.newTaskText("");
    };
    self.removeTask = function(task) { self.tasks.remove(task) };
}

ko.applyBindings(new TaskListViewModel());

This html:

<head>
    <script type="text/javascript" src="jquery-1.7.1.min.js"></script>
    <script type="text/javascript" src="knockout-2.0.0.js"></script>
    <script type="text/javascript" src="script.js"></script>
</head>
<body>
    <h3>Tasks</h3>

    <form data-bind="submit: addTask">
        Add task: <input data-bind="value: newTaskText" placeholder="What needs to be done?" />
        <button type="submit">Add</button>
    </form>

    <ul data-bind="foreach: tasks, visible: tasks().length > 0">
        <li>
            <input type="checkbox" data-bind="checked: isDone" />
            <input data-bind="value: title, disable: isDone" />
            <a href="#" data-bind="click: $parent.removeTask">Delete</a>
        </li> 
    </ul>

    You have <b data-bind="text: incompleteTasks().length">&nbsp;</b> incomplete task(s)
    <span data-bind="visible: incompleteTasks().length == 0"> - it's beer time!</span>
</body>

The example is the same as the one found on the Knockout website, but when I run it, it returns this message on Chrome Fire Bug:

Uncaught TypeError: Cannot read property 'nodeType' of null

This one is related to the knockout file and to this line of my script:

ko.applyBindings(new TaskListViewModel());

And this error is pointing to this line (1766) on knockout:

var isElement = (nodeVerified.nodeType == 1);

What am I doing wrong?

Jeroen
  • 60,696
  • 40
  • 206
  • 339

4 Answers4

178

This problem was happening because I was trying to bind an HTML element before it was created.

My script was loaded on top of the HTML (in the head) but it needed to be loaded at the bottom of my HTML code (just before the closing body tag).

Thanks for your attention James Allardice.

A possible workaround is using defer="defer"

<script src="script.js" type="text/javascript" defer="defer"></script>

Use this if the script is not going to generate any document content. This will tell the browser that it can wait for the content to be loaded before loading the script.

Further reading.

Hope it helps.

Community
  • 1
  • 1
  • 5
    To emphasize: The ` – aliteralmind Sep 20 '14 at 10:18
  • 1
    wonderful, thanks! I just moved my script to the end of body and it worked perfectly. many gratitudes – Eleanor Zimmermann Feb 16 '15 at 21:11
  • Or simply, the element you are trying to bind in, doesn't exist. E.g. `ko.applyBindings(new TaskListViewModel(),document.getElementById('someElementId'));` -- here `someElementId` element doesn't exist when applying the binding due to either **typo** or it is being **binded before it is declared** as mentioned in this answer. – Himanshu Feb 27 '23 at 06:44
33

You might want to consider using the jquery ready handler for this

$(function() {
   function TaskListViewModel() {
   ...
   ko.applyBindings(new TaskListViewModel());
});

Then you achieve two things:

  1. Avoid polluting the global namespace
  2. Knockout binding occurs AFTER the DOM is created. You can place your javascript wherever it is suited for organization.

See http://api.jquery.com/ready/

James Kessler
  • 684
  • 5
  • 9
21

if you have jQuery put apply binding inside onload so that knockout looks for the DOM when DOM is ready.

$(document).ready(function(){
    ko.applyBindings(new TaskListViewModel());
});
Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
Jhankar Mahbub
  • 9,746
  • 10
  • 49
  • 52
5

You have a simple spelling mistake:

self.addTask = fuction() {

Should be:

self.addTask = function() { //Notice the added 'n' in 'function'
James Allardice
  • 164,175
  • 21
  • 332
  • 312