How to overcome this onclick event failure in JS? - javascript

I'm making a simple game. There screen is divided on two and on both sides there are smiling faces generated. Left side, however, contains one more smiling face. After clicking on it, you should get to the next level, where there's 5 more smiling faces on each side. The goal is to find the odd smiling face on the left side each time.
I have this problem, though: Uncaught TypeError: Cannot set property 'onclick' of null
It appears that it's related to this line: theLeftSide.lastChild.onclick = function nextLevel(event){
What's the problem? Here's the rest of my JS part of code.
var numberOfFaces = 5;
var theLeftSide = document.getElementById("leftSide");
var theRightSide = document.getElementById("rightSide");
var theBody = document.getElementsByTagName("body")[0];
function generateFaces(){
var i = 0;
while(i < numberOfFaces){
var top = Math.random() * 400;
top = Math.floor(top);
var left = Math.random() * 400;
left = Math.floor(left);
var face = document.createElement("img");
face.setAttribute("src", "http://home.cse.ust.hk/~rossiter/mooc/matching_game/smile.png");
face.style.top = top + "px";
face.style.left = left + "px";
theLeftSide.appendChild(face);
i++;
}
leftSideImages = theLeftSide.cloneNode(true);
leftSideImages.removeChild(leftSideImages.lastChild);
theRightSide.appendChild(leftSideImages);
};
theLeftSide.lastChild.onclick = function nextLevel(event){
event.stopPropagation();
while (theLeftSide.firstChild){
theLeftSide.removeChild(theLeftSide.firstChild);
}
while (theRightSide.firstChild){
theRightSide.removeChild(theRightSide.firstChild);
}
numberOfFaces += 5;
generateFaces();
};
theBody.onclick = function gameOver(){
alert("Game Over!");
theBody.onclick = null;
theLeftSide.lastChild.onclick = null;
};
I'd be very grateful for your help. Thank you.

Last child returns null if the DOM element has no children. Check to make sure that the "#leftSide" DOM element has a child.
From Mozilla Developer Network:
The Node.lastChild read-only property returns the last child of the node. If its parent is an element, then the child is generally an element node, a text node, or a comment node. It returns null if there are no child elements.

Related

JS for -loop always use last element - scope issues

var url_array = ["ulr1", "ulr2", "ulr3", "ulr4", "ulr5"];
var img = document.createElement('img');
for (i = 0; i < url_array.length; i++){
var div = document.createElement('div');
div.setAttribute("id", "div"+i);
document.getElementById('main').appendChild(div);
document.getElementById('div'+i).style.width = ImageWidth+5+"px";
document.getElementById('div'+i).style.height = ImageHeight+5+"px";
console.log("\ncreate all the DIVs. \nFor loop count: "+i);
img.src = loadImage(url_array[i], ImageWidth, ImageHeight);
try{throw img}
catch(c_img) {
document.getElementById('div'+i).appendChild(c_img);
console.log("after load, and append, in Catch: "+img.src);
console.log("div NR = "+document.getElementById('div'+i).id);
console.log(document.getElementById('div'+i).childNodes.length);
} //catch
} // FOR
function loadImage(URL, h, w)
{
console.log("loadImage callaed with URL = "+URL);
return url = URL+Date.now().toString(10);
}
For-Loop is supposed to retrieve urls address of an images (from camera) and append them to DIV. DIV and IMGs are created on the fly. Problem is that only last DIV become image holder.
I am using catch-try to force execute code immediately so each separate Div+i will have distinctive image. Also construction like this one (immediately invoked function expression):
(function(){
something here;
})();
for creating "private" scope gives no hope. Either "Let" - which supposed to define variable locally is not helping. *My knowledge here is limited and I'm relying on data found on web site (can give link later if it is not against rules).
Output of console.log() is not helping much.
Everything goes as it should, except for the childNodes.length which is 1 for each for-loop iteration - that means each DIV have its IMG child I guess... If so - why I can't see them?
I feel I am close to what I want to achieve - using Intervals() refresh each DIV with new camera snapshot, but I need to solve this current issue.
Example of code ready to use:
js.js:
var url_array = [
"https://images.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg",
"https://images.pexels.com/photos/45201/kitty-cat-kitten-pet-45201.jpeg",
"https://images.pexels.com/photos/617278/pexels-photo-617278.jpeg"
];
var ImageWidth = 640;
var ImageHeight = 480;
var img = document.createElement('img');
for (i = 0; i < url_array.length; i++){
var div = document.createElement('div');//.setAttribute("id", "div0");
div.setAttribute("id", "div"+i);
document.getElementById('main').appendChild(div);
document.getElementById('div'+i).style.width = ImageWidth+5+"px";
document.getElementById('div'+i).style.height = ImageHeight+5+"px";
var color = ((Math.floor(Math.random() * (16777215)) + 1).toString(16)); // from 1 to (256^3) -> converted to HEX
document.getElementById('div'+i).style.background = "#"+color;
document.getElementById('div'+i).style.width = ImageWidth+5+"px";
document.getElementById('div'+i).style.height = ImageHeight+5+"px";
console.log("\ncreate all the DIVs. \nFor loop count: "+i);
img.src = loadImage(url_array[i], ImageWidth, ImageHeight);
try{throw img}
catch(c_img) {
document.getElementById('div'+i).appendChild(c_img);
console.log("after load, and append, in Catch: "+img.src);
console.log("div NR = "+document.getElementById('div'+i).id);
console.log(document.getElementById('div'+i).childNodes.length);
} // catch
} // FOR
function loadImage(URL, h, w)
{
console.log("loadImage callaed with URL = "+URL);
return url = URL+"?auto=compress&h="+h+"&w="+w;
}
HTML:
<html>
<head>
<meta charset="utf-8">
<STYLE>
div#main {
padding: 5px;
background: black;
}
</STYLE>
</head>
<body>
<script type="text/javascript">
function downloadJSAtOnload() {
var element = document.createElement("script");
element.src = "js.js";
document.body.appendChild(element);
}
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;
</script>
<div id="main"></div>
</body>
</html>
The problem is that your
var img = document.createElement('img');
is outside the loop - you're only ever creating one img. When appendChild is called on an element that already exists in the DOM (such as on the second, third, fourth, etc iteration), the element gets removed from its previous location and inserted into the new location.
Create the image inside the loop instead, and try not to implicitly create global variables - when declaring new variables, always use let or const.
Also, when you do
var div = document.createElement('div');
you have a reference to the div you just created - there's no need to assign an id to it in order to select it with
document.getElementById('div'+i)
in below lines in the same scope. Instead, just keep referencing the div:
const ImageWidth = 200;
const ImageHeight = 200;
const main = document.getElementById('main');
var url_array = ["ulr1", "ulr2", "ulr3", "ulr4", "ulr5"];
for (let i = 0; i < url_array.length; i++){
const img = document.createElement('img');
const div = document.createElement('div');
main.appendChild(div);
div.style.width = ImageWidth+5+"px";
div.style.height = ImageHeight+5+"px";
img.src = loadImage(url_array[i], ImageWidth, ImageHeight);
div.appendChild(img);
}
console.log(main.innerHTML);
function loadImage(URL, h, w) {
return URL+Date.now().toString(10);
}
div#main {
padding: 5px;
background: black;
}
<div id="main">
</div>

Forbid some div with class accurate for moving

I created a turn-based javascript game.For moving the players I use jQuery. I'm moving a sprite inside my game board which consists of 100 squares with board-0 ids to board-99. I'm moving my sprite from id to id. Everything works perfectly, but I can not prohibit certain id's to travel. All the boxes with a class "accesOn" are ok all those with the class "accesOff" should not be used. I managed to block the upward movement, if the top position to a "accesOff" class at the start of the game. The problem is that after my sprite never goes up again. Could you help me to make sure not to move on all the divs that have an "accesOff" class?
Thanks
/*********RECUP. ID POS. TOP before move******** */
var nombreIdPos = maDivId.substr(6);
var posActuelleNombreId = parseInt(nombreIdPos);
var posDessusNombreId = posActuelleNombreId -= 10;
var $posDessusId = 'board-' + posDessusNombreId;
//******** MOVE
function main() {
var $nombreId = maDivId.substr(6);
var $posDiv1 = parseInt($nombreId);
var $currentPosId = maDivId; //TEST voir si besoin plus tard
var $largeur = ($('#contenu').width());
var $hauteur = ($('#contenu').height());
$(window).on('keydown', function (e) {
var touche = e.which;
switch (touche) {
case 38: //TOP
var $idDivDessus = $('#' + $posDessusId);
if ($idDivDessus.hasClass('accesOn')) { // this if is not good
$posX = '0%';
$posY = '100%';
var $newPosH = $posDiv1 -= 10;
var $newPos = $("#board-" + $newPosH);
$($newPos).css('background', $player1 + $posX + $posY);
$('.joueur1').css('background', "").removeClass('joueur1');
$($newPos).addClass('joueur1');
} //****FIN IF
} //FIN if 1er
break;
}); // FIN Event keydown
} //FIN main function
$(document).ready(main);
You only care about the tile you are moving to, you don't have to check for access on the tile you are already on (assume you handled this correctly before arriving on the current tile).
Add a check for the tile you want to move to, if it has the class accessOff simply avoid moving, no other actions should be required.
example check for //TOP
$posX = '0%';
$posY = '66%';
$newPos = $posDiv1 -= 10;
$newPos = $("#board-" + $newPos);
if(!$newPos.hasClass('accessOff')){ //check here for accessOff
$($newPos).css('background', $player1 + $posX + $posY);
$('.joueur1').css('background', "").removeClass('joueur1');
$($newPos).addClass('joueur1');
console.log('Variable $newPosR = ' + $newPos);
} else {
$posDiv1 -= change;
}
I would recommend creating a function to move your player to get rid of some code duplication. It will make your life easier in the future.
Something like this:
function MovePlayer(x, y, change) {
$newPos = $posDiv1 += change;
$newPos = $("#board-" + $newPos);
if(!$newPos.hasClass('accessOff')){
$($newPos).css('background', $player1 + x + y);
$('.joueur1').css('background', "").removeClass('joueur1');
$($newPos).addClass('joueur1');
console.log('Variable $newPosR = ' + $newPos);
} else {
$posDiv1 -= change;
}
}
Your //TOP would then just be MovePlayer('0%', '66%', -10), and you can reuse that function to move any direction.

Javascript: move new element after createElement

I have created a zombie img in my banner div but I can't get the img to move to the left after it has been created.
createZombie function is on a timer:
createZombieTimer = window.setInterval(createZombie, 1000);
That is in an init() that loads with the body.
function createZombie(){
var imgElem = document.createElement("img");
imgElem.src = "img/zombie_walk_right.gif";
var newZom = document.getElementById('banner').appendChild(imgElem);
newZom.style.height = "40px";
newZom.style.width = "auto";
newZom.style.display = "block";
newZom.style.marginLeft = "50px";
var zomPos = parseInt(newZom.style.marginLeft);
if (zomPos > 0) {
newZom.style.marginLeft = (zomPos + 50) + "px";
}
}
Since it's clear from the comments now what you want to do, I'll post it here as the answer.
You can do this with plain javascript as well. What you will have to do, is use setInterval(). This will execute a function every second, in which you can update the position of the elements.
Example:
// Function to move the zombie by 50 pixels
function moveZombie()
{
// Select the zombie element (will need additional logic to select all of them, just an example)
zomElement = document.getElementById('zombie1');
zomPos = parseInt(zomElement.style.marginLeft);
zomElement.style.marginLeft = (zomPos + 50) + 'px';
}
// Call this function every second
setInterval(moveZombie(), 1000);

Why does my setInterval function not work second time? (JavaScript)

I made a div and I want to make it animate in a specific direction every 1 second. In the code that I have provided there's a function called resetPosition() don't worry about that, it's from my other js file that I linked in the head section(That works perfectly). I just want to know why setInterval() doesn't work correctly?
Here's my code:-
<!DOCTYPE html>
<html>
<head>
<title>Animtion</title>
<link rel="icon" href="http://3.bp.blogspot.com/-xxHZQduhqlw/T-cCSTAyLQI/AAAAAAAAAMw/o48rpWUeg2E/s1600/html-logo.jpg" type="image/jpg">
<script src="easyJs.js"></script>
<style type="text/css">
div{height:100px; width:100px; background:cyan;}
</style>
</head>
<body onload="">
<div id="demo"></div>
</body>
<script type="text/javascript">
setInterval(function(){var x = new element('demo');
x.resetPosition('absolute', '100px', '10px');}, 1000);
</script>
</html>
Here's easyJs:-
function element(elementId){
this.myElement = document.getElementById(elementId);
this.resetColor = changeColor;
this.resetSize = changeSize;
this.resetBackground = changeBackground;
this.resetPosition = changePosition;
this.resetBorder = changeBorder;
this.resetFontFamily = changeFont;
this.resetFontSize = changeFontSize;
this.resetFontStyle = changeFontStyle;
this.resetZindex = changeZindex;
this.resetClass = changeClass;
this.resetTitle = changeTitle;
this.resetMarginTop = changeMarginTop;
this.resetMarginLeft = changeMarginLeft;
this.resetSource = changeSource;
this.resetInnerHTML = changeInnerHTML;
this.resetHref = changeHref;
this.resetTextDecoration = changeTextDecoration;
this.resetFontWeight = changeFontWeight;
this.resetCursor = changeCursor;
this.resetPadding = changePadding;
this.getValue = getTheValue;
this.getName = getTheName;
this.getHeight = getTheHeight;
}
function changeColor(color){
this.myElement.style.color = color;
}
function changeSize(height, width){
this.myElement.style.height = height;
this.myElement.style.width = width;
}
function changeBackground(color){
this.myElement.style.background = color;
}
function changePosition(pos, x, y){
this.myElement.style.position = pos;
this.myElement.style.left = x;
this.myElement.style.top = y;
}
function changeBorder(border){
this.myElement.style.border = border;
}
function changeFont(fontName){
this.myElement.style.fontFamily = fontName;
}
function changeFontSize(size){
this.myElement.style.fontSize = size;
}
function changeZindex(indexNo){
this.myElement.style.zIndex = indexNo;
}
function changeClass(newClass){
this.myElement.className = newClass;
}
function changeTitle(newTitle){
this.myElement.title = newTitle;
}
function changeMarginTop(top){
this.myElement.style.marginTop = top;
}
function changeMarginLeft(left){
this.myElement.style.marginLeft = left;
}
function changeSource(newSource){
this.myElement.src = newSource;
}
function changeHref(newHref){
this.myElement.href = newHref;
}
function changeInnerHTML(newHTML){
this.myElement.innerHTML = newHTML;
}
function changeTextDecoration(decoration){
this.myElement.style.textDecoration = decoration;
}
function changeFontWeight(weight){
this.myElement.style.fontWeight = weight;
}
function changeFontStyle(style){
this.myElement.style.fontStyle = style;
}
function changeCursor(cursor){
this.myElement.style.cursor = cursor;
}
function changePadding(padding){
this.myElement.style.padding = padding;
}
function getTheValue(){
var theValue = this.myElement.value;
return theValue;
}
function getTheName(){
var theName = this.myElement.name;
return theName;
}
function getTheHeight(){
var theHeight = this.myElement.offsetHeight;
return theHeight;
}
This might help you see what and how. (Scroll down to bottom of code):
Fiddle
// Create a new EasyJS object
var el = new element('demo');
// x and y position of element
var x = 0, y = 0;
// Interval routine
setInterval(function(){
x = x + 1; // Increment x by 1
y = y + 1; // Increment y by 1
// Move element to position xy
el.resetPosition('absolute', x + 'px', y + 'px');
// Add text inside element showing position.
el.resetInnerHTML(x + 'x' + y);
// Run five times every second
}, 1000 / 5);
Explanation of original code:
setInterval(function() {
// Here you re-declare the "x" object for each iteration.
var x = new element('demo');
// Here you move the div with id "demo" to position 100x10
x.resetPosition('absolute', '100px', '10px');
// Once every second
}, 1000);
The HTML div element demo initially has no positioning styling (CSS). As such it is rendered in the default position according to the browser defaults.
In first iteration you change the style option position to absolute. That means you can move it anywhere. Secondly you move it to offset 100x10.
On the second and every iteration after that the element has position set to absolute, and it reside at 100x10. Then you say it should be moved to 100x10 – as in same place as it is.
If you do not change either x or y position (left / top), it will stay at 100x10, no mather how many times you run the loop. Run it 100 times a second for a year and it will still be at 100x10 ;-)
Think about it. Everytime the interval runs, it creates a new instance of element "demo".
This demo variable has all the default values your elements object sets it to (if any), and runs the same function each time. That's why it only moves once.
Hoist your element higher and you won't be re-declaring each interval.
<script type="text/javascript">
var x = new element('demo');
setInterval(function(){
x.resetPosition('absolute', '100px', '10px');}, 1000);
</script>
The problem isn't in the setInterval, because a copypasta'd fiddle of the same expression worked properly, so it's probably either an issue with resetPosition or maybe easyJS, though I doubt the latter.
Also, it's unclear whether demo appears on-page, as it's not added anywhere visibly.
EDIT:
If demo is appended somewhere behind the scenes, it's still piling a new demo on that spot every second

easeljs add bitmap to stage multiple time at different position

I just started working with the easeljs library. I have created a canvas with a stage and it works.
I want to add bitmaps as buttons and add them side by side in the bottom on the stage.
I have tried with the following, but it place the bitmaps on top on each other.
var button = new createjs.Bitmap(queue.getResult("largebrick"));
var contentWidth = document.getElementById("content").offsetWidth;
var contentHeight = document.getElementById("content").offsetHeight;
var container = new createjs.Container();
stage.addChild(container);
stage.enableMouseOver(10);
for(var i = 0; i <10; i++){
button.x = 0 + button.getBounds().width * i;
button.y = contentHeight - button.getBounds().height;
button.addEventListener("mouseover", function() { document.body.style.cursor = 'pointer'; });
button.addEventListener("mouseout", function() { document.body.style.cursor = 'default'; });
button.addEventListener("click", function(event) {addBricks(button); });
container.addChild(button);
}
I hope some of you guys can help.
A couple things here. You need to create a new instance of the button inside the loop, otherwise you are just moving the original instance of button on each iteration. In this example, I simplified everything a bit by using a shape, but you could easily do the same with your Bitmap.
var xPos = 0;
for(var i = 0; i < 10; i++){
var button = new createjs.Bitmap(queue.getResult("largebrick"));
button.cursor = "pointer";
button.x = xPos;
button.y = contentHeight - 50;
xPos += 60;
container.addChild(button);
}
Also, createjs supports defining cursors as a property of a display object like so
button.cursor = "pointer";
which should simplify your code a little.

Categories