为什么 IIFE 可以修复 "cannot set property 'onclick' of null"

Why IIFE can fix "cannot set property 'onclick' of null"

我有一段代码如下

<body id="main">
<h1>Matching Game</h1>
<p>Click on the extra smile face on the left</p>

<div id="left_side"></div>
<div id="right_side"></div>

<script>    
        var theLeftSide = document.getElementById("left_side");
        var theRightSide = document.getElementById("right_side");

         theLeftSide.lastChild.onclick = function nextLevel(event){            
            event.stopPropagation();
            quan+=5;
            dele();
            generateFace();
            }     

</script>
</body>

这一行theLeftSide.lastChild.onclick会报错,说"cannot set property 'onclick' of null",因为在javascript引擎扫描它的时候,它还没有任何child然而。这个问题可以通过将这段代码放在$(document).ready里面来解决,我知道。

但是,我也尝试了 IIFE,这意味着将所有这些代码包装在

(function(){
  var theLeftSide = document.getElementById("left_side");
  var theRightSide = document.getElementById("right_side");

     theLeftSide.lastChild.onclick = function nextLevel(event){            
        event.stopPropagation();
        quan+=5;
        dele();
        generateFace();
        }     
})()

并且还看到问题已解决,但无法弄清楚原因。谁能给我解释一下?

完整的原始代码如下:

     <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>part4</title>
<style>
div{
    position:absolute;
    }
#left_side{
    width:500px; height: 500px; border-right:#000 thin inset;
    float: left;
    }
#right_side{
    width:500px; height: 500px;
    float:left; left:500px;
    }
img{
    position: absolute;
    }
body{
    margin:0px;
    }

</style>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.3.min.js"></script>
</head>

<body onload="generateFace()" id="main">
<h1>Matching Game</h1>
<p>Click on the extra smile face on the left</p>

<div id="left_side"></div>
<div id="right_side"></div>

<script>
var theLeftSide = document.getElementById("left_side");
var theRightSide = document.getElementById("right_side");
var e = event;
var theBody= document.getElementById("main");

var post_left =0;
var post_top=0;
var quan = 4;
function generateFace(){
    var i =0;
    for(i=0; i<= quan; i++){
        var img = document.createElement("img");
        img.src="smile.png"; post_top=getRandom(); post_left=getRandom();
        img.style.top = post_top + "px";
        img.style.left = post_left +"px";
        theRightSide.appendChild(img);
        var clone = img.cloneNode(true);
        theLeftSide.appendChild(clone);
        }
        //var clone = theRightSide.cloneNode(true);
        //theLeftSide.appendChild(clone);
        var extra = document.createElement("img");
        extra.src="smile.png"; post_top=getRandom(); post_left=getRandom();
        extra.style.top = post_top + "px";
        extra.style.left = post_left +"px";
        theLeftSide.appendChild(extra);
    };

function getRandom(){
    var i = Math.random() * 400 +1;
    return Math.floor(i);
    };
function dele(){
    while(theLeftSide.firstChild != null){
        theLeftSide.removeChild(theLeftSide.firstChild);
        }
    while(theRightSide.firstChild != null){
        theRightSide.removeChild(theRightSide.firstChild);
        }
    };
theBody.onclick = function gameOver() {
    alert("Game Over!");
    dele();
    theBody.onclick = null;
    theLeftSide.lastChild.onclick = null;
};
function nextLevel(event){
    event.stopPropagation();
    quan+=5;
    delse();
    generateFace();
    }
</script>
</body>
</html>

和 IIFE:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>part4</title>
<style>
div{
    position:absolute;
    }
#left_side{
    width:500px; height: 500px; border-right:#000 thin inset;
    float: left;
    }
#right_side{
    width:500px; height: 500px;
    float:left; left:500px;
    }
img{
    position: absolute;
    }
body{
    margin:0px;
    }

</style>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.3.min.js"></script>
</head>

<body id="main">
<h1>Matching Game</h1>
<p>Click on the extra smile face on the left</p>

<div id="left_side"></div>
<div id="right_side"></div>

<script>
    (function(){
        var theLeftSide = document.getElementById("left_side");
        var theRightSide = document.getElementById("right_side");
        var e = event;
        var theBody= document.getElementById("main");

        var post_left =0;
        var post_top=0;
        var quan = 4;

        generateFace();

        function generateFace(){
            var i =0;
            for(i=0; i<= quan; i++){
                var img = document.createElement("img");
                img.src="smile.png"; post_top=getRandom(); post_left=getRandom();
                img.style.top = post_top + "px";
                img.style.left = post_left +"px";
                theRightSide.appendChild(img);
                var clone = img.cloneNode(true);
                theLeftSide.appendChild(clone);
                }
                //var clone = theRightSide.cloneNode(true);
                //theLeftSide.appendChild(clone);
                var extra = document.createElement("img");
                extra.src="smile.png"; post_top=getRandom(); post_left=getRandom();
                extra.style.top = post_top + "px";
                extra.style.left = post_left +"px";
                theLeftSide.appendChild(extra);
        };

        function getRandom(){
        var i = Math.random() * 400 +1;
        return Math.floor(i);
        };

        function dele(){
            while(theLeftSide.firstChild != null){
                theLeftSide.removeChild(theLeftSide.firstChild);
                }
            while(theRightSide.firstChild != null){
                theRightSide.removeChild(theRightSide.firstChild);
                }
            };
        theBody.onclick = function gameOver() {
            alert("Game Over!");
            dele();
            //theBody.onclick = undefined;
            //theLeftSide.lastChild.onclick = undefined;
        };

        theLeftSide.lastChild.onclick = function nextLevel(event){            
            event.stopPropagation();
            quan+=5;
            dele();
            generateFace();
            } 
    })();

</script>
</body>
</html>

在原始代码段中,generateFace() 在页面加载时被调用。第二段代码直接调用了generateFace()。与 IIFE 没有关系。请忘记它! :-P 这是您在每种情况下所做操作的简化版本(在 运行 片段之前打开浏览器控制台:

原始代码(抛出类型错误)

console.log('attempting to initialize onclick');
document.getElementById('clickable').onclick = function () {
  alert(this.id);
};
console.log('onclick initialized successfully');

function gendiv () {
  console.log('gendiv was called');
  document.body.innerHTML = '<div id="clickable">click me</div>';
}
<body onload="gendiv()"></body>

修改后的代码

gendiv();

console.log('trying to initialize onclick');
document.getElementById('clickable').onclick = function () {
  alert(this.id);
};
console.log('onclick initialized successfully');

function gendiv () {
  console.log('gendiv was called');
  document.body.innerHTML = '<div id="clickable">click me</div>';
}
<body></body>