Search code examples
javascriptangularjstodomvc

Applying document.body.innerHTML to element disables AngularJS functionality


Why would doing this to the angular todo app stop all functionality?

document.body.onload = function addElement() {
    document.body.innerHTML +=
        '<div  >'+
        '</div> ';
};

All the buttons with ng-click don't call their respective scope functions and pressing enter on the input causes a page reload instead of adding a new todo item.

demo commit:https://github.com/QuantumInformation/todomvc/commit/95ca6233a3e5b3a9775675c3f92d731ecc6032af


Solution

  • Keep in mind that the question is referring to a utility that is supposed to work across any website, not just an Angular website. If you found this question and are building an Angular app this does NOT apply to you. It's a very specific use case outside the world of building AngularJS apps.

    I think @epascarello explained it pretty well in a comment, but to reiterate:

    What you’re doing is taking the HTML code currently on the page (which does NOT include event listeners, $scope bindings, etc) and re-rendering a wiped version of it, with a <div> at the end.

    If you have a directive with the following template:

    <div>
        <p>{{person.name}}</p>
    </div>
    

    when Angular displays that on the page it compiles it by binding it to a $scope. So the HTML on the actual page would be:

    <div>
        <p>Nikos</p>
    </div>
    

    and AngularJS will update that element when person.name changes. How that happens is Angular is storing a reference to the DOM element. Not the string value, the actual object. However, when you do document.body.innerHTML = document.body.innerHTML += '<div></div>', you're taking the HTML and creating a duplicate copy of that. Now, the DOM element AngularJS has the reference to doesn't exist. There is a new version being shown on the page, but AngularJS doesn't know about it.

    The proper way to do this would be:

    document.body.appendChild(document.createElement('div'));