I am making a magic squares game with javascript. When I click on an image and the function move is invoked I get the following error:

Message: 'target.id' is null or not an object
Line: 32
Char: 2
Code: 0

As you can see in my html the images do have id. So I don't understand why I get this error. I have also tried only writing var id = event.target.id; and I get the same error. I have made another game where I also use target.id and have an almost identical html and that works fine.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Memory</title>
<link href="style.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="patch.js"></script>
</head>

<body>
<div id="squares">
  <ul>
    <li><img src="img/box0.png" alt="Square 1" width="50" height="50" id="s1" /></li>
    <li><img src="img/box0.png" alt="Square 2" width="50" height="50" id="s2" /></li>
    <li><img src="img/box0.png" alt="Square 3" width="50" height="50" id="s3" /></li>
    <li><img src="img/box0.png" alt="Square 4" width="50" height="50" id="s4" /></li>
    <li><img src="img/box0.png" alt="Square 5" width="50" height="50" id="s5" /></li>
    <li><img src="img/box0.png" alt="Square 6" width="50" height="50" id="s6" /></li>
    <li><img src="img/box0.png" alt="Square 7" width="50" height="50" id="s7" /></li>
    <li><img src="img/box0.png" alt="Square 8" width="50" height="50" id="s8" /></li>
    <li><img src="img/box0.png" alt="Square 9" width="50" height="50" id="s9" /></li>
    <li><img src="img/box0.png" alt="Square 10" width="50" height="50" id="s10" /></li>
    <li><img src="img/box0.png" alt="Square 11" width="50" height="50" id="s11" /></li>
    <li><img src="img/box0.png" alt="Square 12" width="50" height="50" id="s12" /></li>
    <li><img src="img/box0.png" alt="Square 13" width="50" height="50" id="s13" /></li>
    <li><img src="img/box0.png" alt="Square 14" width="50" height="50" id="s14" /></li>
    <li><img src="img/box0.png" alt="Square 15" width="50" height="50" id="s15" /></li>
    <li><img src="img/box0.png" alt="Square 16" width="50" height="50" id="s16" /></li> 
  </ul> 
</div>
<div id="control">
<input type="button" id="button" value="Ny omgång" />
</div>

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

</body>
</html>

// JavaScript Document

//Variabler
var numbers = new Array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); //Array med möjliga nummer för brickorna.
var squares = new Array(); //Array med brickor.
var attempts; //Antalet försök/rundor.
var hole = new Object; //Platsen utan bricka.
var temp = new Object; //Platshållare vid förflyttningar

//Blandar arrayen med nummer.
function shuffle(){
  var x = numbers.length;
  while(--x){
     var y = Math.floor(Math.random() * (x + 1));
     var i = numbers[x];
     var j = numbers[y];
     numbers[x] = j;
     numbers[y] = i;
  }
}

//Representerar en bricka.
function Square(pos, number, x, y){
    this.pos = pos;
    this.number = number;
    this.x = x;
    this.y = y;
}

//Flyttar en bricka
function move(event){   
    var square = squares[event.target.id.substring(1)];
    if(adjacentToHole(square)){
          temp.x = square.x;
          temp.y = square.y;
          temp.pos = square.pos;
          square.x = hole.x;
          square.y = hole.y;
          square.pos = hole.pos;
          hole.x = temp.x;
          hole.y = temp.y;
          hole.pos = temp.pos;
          document.getElementById('s' + square.pos).setAttribute('src', 'img/box' + square.number + '.png');
          document.getElementById('s' + hole.pos).setAttribute('src', 'img/box0.png');
          if(finished()){
              end();
          }
     }  
}

function adjacentToHole(square){
    if((square.x == hole.x - 1 || square.x == hole.x + 1) && (square.y == hole.y - 1 || square.y == hole.y + 1)){
        return true;
    }
    else{
        return false;
    }
}

function finished(){
    for(var x = 1; x <= 15; x++){
        if(square[x].pos != square[x].number){
            return false;
        }
    }   
    return true;
}

//Vid spelets avslut ska antalet förflyttningar visas
function end(){
    alert("Du klarade av spelet på " + attempts + " rundor!");
}

//Startar ett nytt spel
function start(){
    shuffle();
    attempts = 0;
    hole.pos = 16;
    hole.x = 4;
    hole.y = 4;
    document.getElementById('s16').setAttribute('src', 'img/box0.png');
    var i = 1;
    var j = 1;
    for(var x = 1; x <= 15; x++){
        document.getElementById('s' + x).setAttribute('src', 'img/box' + numbers[x-1] + '.png');
        squares[numbers[x-1]] = new Square(x, numbers[x-1],i,j);
        if(++i > 4){
            i = 1;
            j++;
        }
    }   
}

//När sidan laddas ska event kopplas till objekt och en ny omgång startas
setEventById('button', 'click', start);
for(var x = 1; x <= 15; x++){
        setEventById('s' + x, 'click', move);
}
start();

The setEventById method from patch.js:

function setEventById(id, ev, fu) {
  if(dom2) {
    document.getElementById(id).addEventListener(ev, fu, false);
  }

  if(ie) {
    document.getElementById(id).attachEvent("on" + ev, fu);
  }
}

Maddie,

You probably just need a couple of standard lines of code for cross-browser compatibility.

function move(evt){
  evt = evt || window.event;
  var target = evt.target || evt.srcElement;
  ...

It worked, but another problem emerged. I now get the same error when using getElementById instead. I did not have any problem with that before.

Message: 'document.getElementById(...)' is null or not an object
Line: 122
Char: 5
Code: 0

I have made some minor changes so I will post the new code. I moved the setEvenById into this file for visibility.

// JavaScript Document

//Variabler
var numbers = new Array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); //Array med möjliga nummer för brickorna.
var squares = new Array(); //Array med brickor.
var attempts; //Antalet försök/rundor.
var hole = new Object; //Platsen utan bricka.
var temp = new Object; //Platshållare vid förflyttningar

//Blandar arrayen med nummer.
function shuffle(){
  var x = numbers.length;
  while(--x){
     var y = Math.floor(Math.random() * (x + 1));
     var i = numbers[x];
     var j = numbers[y];
     numbers[x] = j;
     numbers[y] = i;
  }
}

//Representerar en bricka.
function Square(pos, number, x, y){
	this.pos = pos;
	this.number = number;
	this.x = x;
	this.y = y;
}

//Flyttar en bricka om den ligger bredvid hålet
function move(evt){	
	evt = evt || window.event;
  	var target = evt.target || evt.srcElement;
	var id = target.id.substring(1);
	//Om det inte är hålet hämtas brickan
	if(id != hole.pos){
	  var square = getSquareByPos(id);
	  //Om rickan är bredvid hålet flyttas den
	  if(adjacentToHole(square)){
		  temp.x = square.x;
		  temp.y = square.y;
		  temp.pos = square.pos;
		  square.x = hole.x;
		  square.y = hole.y;
		  square.pos = hole.pos;
		  hole.x = temp.x;
		  hole.y = temp.y;
		  hole.pos = temp.pos;
		  document.getElementById('s' + square.pos).setAttribute('src', 'img/box' + square.number + '.png');
		  document.getElementById('s' + hole.pos).setAttribute('src', 'img/box0.png');
		  attempts++;
		  //Om alla brickor ligger på rätt plats är spel slut
		  if(finished()){
			  end();
		  }
	   }	
	}
}

//ämtar en bricka baserat på dess position
function getSquareByPos(position){
	for(var x = 0; x < 15; x++){
		if(squares[x].pos == position)
			return squares[x];
	}
}

//Kollar om en bricka är bredvid hålet
function adjacentToHole(square){
	if( ((square.x == hole.x - 1 || square.x == hole.x + 1) && (square.y == hole.y )) || ((square.y == hole.y - 1 || square.y == hole.y + 1) && (square.x == hole.x)) ){
		return true;
	}
	else{
		return false;
	}
}

//Kollar om alla brickor ligger på rätt plats
function finished(){
	for(var x = 0; x < 15; x++){
		if(squares[x].number != squares[x].pos + 1 ){
			return false;
		}
	}	
	return true;
}

//Vid spelets avslut ska antalet förflyttningar visas
function end(){
	alert("Du klarade av spelet på " + attempts + " förflyttningar!");
}

//Startar ett nytt spel
function start(){
	shuffle();
	attempts = 0;
	hole.pos = 15;
	hole.x = 4;
	hole.y = 4;
	document.getElementById('s15').setAttribute('src', 'img/box0.png');
	var i = 1;
	var j = 1;
	for(var x = 0; x < 15; x++){
		document.getElementById('s' + x).setAttribute('src', 'img/box' + numbers[x] + '.png');
		squares[x] = new Square(x, numbers[x],i,j);
		if(++i > 4){
			i = 1;
			j++;
		}
		
	}	
}

ie = (document.all) ? true:false; // ie4+
dom2 = ((document.getElementById)&&(!ie))?true:false; // mozilla, netscape 6+ osv.
function setEventById(id, ev, fu) {
  if(dom2) {
    document.getElementById(id).addEventListener(ev, fu, false);
  }

  if(ie) {
    document.getElementById(id).attachEvent("on" + ev, fu);
  }
}

//När sidan laddas ska event kopplas till objekt och en ny omgång startas
setEventById('button', 'click', start);
for(var x = 0; x < 16; x++){
		setEventById('s' + x, 'click', move);
}
start();

Maddie,

I don't know which line is #122 but here's how to debug it:

Split the statement as follows:

var id = expression;
var element = document.getElementById(id);
if(element) {
  expression;
}
else {
  alert(id);
}

This should give you a clue as to why it gives an error.

It's good practise always to test the result of document.getElementById() before using it, whether you are debugging or not.

I wrote code for a magic square once. If I recall correctly, I didn't need to use getElementById, which is inefficient especially when called inside event handlers or loops. It doesn't matter too much in a small thing like this but in a large web page with many nodes, getElementById can be noticably slow.

Option 1: Use document.getElementsByTagName() to find all the img tags in one hit, then loop through the resulting "collection" to attach the onclick handlers.

Option 2: Create the img nodes in javascript, not HTML. Use document.createElement() followed by container.appendChild() to insert them into the document. By keeping references to the elements as they are created (eg in an array) then you don't need to rediscover them from the DOM.

Airshow

Thank you so much. I had tried debugging in other ways to no success, but now I found the problem.

And thanks for the additional info about alternatives to getElementById. That is very useful.

:cool:

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.