correct way for onClick Binding in Button - javascript

I am working on a coding exercise and I was done with it and tried to play around a little.
This is the html i have:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<h1>Test</h1>
<script src="jquery.js"></script>
<script src="test.js"></script>
<script>
function runAfter() {
alert("HI!");
var p = document.createElement("p");
p.id = "msg";
p.innerHTML = "TEST!";
document.body.innerHTML += "<br />";
document.body.appendChild(p);
console.log(p.innerHTML);
};
//window.onload=runAfter;
</script>
</body>
</html>
And the js file:
"use strict";
var Widget = {
init: function Widget(width, height) {
this.width = width || 50;
this.height = height || 50;
this.$elem = null;
},
render: function ($where) {
if (this.$elem) {
this.$elem.css({
width: this.width + "px",
height: this.height + "px"
}).appendTo($where);
}
}
};
var Button = Object.create(Widget);
Button.config = function config(width, height, label) {
this.init(width, height);
this.label = label || "ClickMe";
this.$elem = $("<button>").text(this.label);
};
Button.build = function ($where) {
this.render($where);
this.$elem.click(this.onClick.bind(this));
};
Button.onClick = function (evt) {
console.log("Button '" + this.label + "' clicked!");
this.updateMessage("Button '" + this.label + "' clicked!");
};
Button.updateMessage = function (msg) {
var p = document.getElementById("msg");
p.innerHTML = msg;
};
function run() {
let $body = $(document.body);
//create
let btn1 = Object.create(Button);
let btn2 = Object.create(Button);
//initialize
btn1.config(125, 30, "Hello");
btn2.config(150, 40, "World");
//build
btn1.build($body);
btn2.build($body);
};
$(document).ready(run);
if you leave //window.onload=runAfter; as is in html, you can see the problem in the console that comes from Button.onClick function. But the console.log works giving
"Button '" + this.label + "' clicked!"
But if you uncomment it, that function doesn't work.
What is going wrong? Is the dynamic this binding going out of scope to somewhere else ?
The runAfter() appends elements to dynamically generated html page from test.js.
What should happen is, the two buttons should work as expected with console.log and <p> para with id=msg printing out the same as console.log

It works if you comment out
document.body.innerHTML += "<br/>";
and replace it with
document.body.appendChild(document.createElement("br"));
Edit with explanation:
What's happening is that when you use document.body.innerHTML +=, you're taking the innerHTML as a string, adding to that string, and then replacing the entire innerHTML with the new string. So you're not adding to what's already there (the way appendChild() does), you're removing all of it and replacing it.
When you remove the buttons, the click handlers attached to them are gone. And the scripts don't run again after the innerHTML is replaced, so the click handlers are never reattached.

Related

How to make reusable button

I'm new to JavaScript and I want to create a button that, when clicked, will change the position of the object. But after clicking once, nothing else works. Below I have provided the code with the very essence of the problem, and my code where I want to apply it. Maybe someone will tell you how to make a restart button correctly, instead of constantly creating new divs.
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<style>
button {
position: relative;
}
</style>
</head>
<body>
<button>Add new position</button>
<script>
const btn = document.querySelector('button');
btn.addEventListener('click', () => {
btn.style.top = '50px';
});
</script>
</body>
</html>
MY PROJECT
const yes = document.getElementById('yes');
const no = document.getElementById('no');
const body = document.querySelector('.body');
const message = document.querySelector('.message');
const reset = document.querySelector('.panel__close')
const bodyMessage = document.querySelector('.body__message')
const randomNum = Math.floor(Math.random() *100) +1;
yes.addEventListener('click', () => {
body.innerHTML = '';
const paraLox = document.createElement('p');
paraLox.textContent = 'Congratulation, Jaba Loshara Ebaniy!';
paraLox.style.fontSize = '3rem';
paraLox.style.textAlign = 'center';
paraLox.style.fontWeight = '1000';
paraLox.style.color = 'red';
body.appendChild(paraLox);
reset.addEventListener('click', () => { // reset button
paraLox.parentNode.removeChild(paraLox);
const bodyError = document.createElement('div');
bodyError.setAttribute('class', 'body__error-image');
bodyMessage.appendChild(bodyError);
const bodyErrorImage = document.createElement('img');
bodyErrorImage.setAttribute('src', 'error.png')
bodyErrorImage.style.width = '50px';
bodyErrorImage.style.height = '50px';
bodyError.appendChild(bodyErrorImage);
const bodyText = document.createElement('div');
bodyText.setAttribute('class', 'body__text');
bodyText.textContent = 'Jaba Lox?';
bodyMessage.appendChild(bodyText);
const bodyButtons = document.createElement('div');
bodyButtons.setAttribute('class', 'body__buttons');
bodyMessage.appendChild(bodyButtons);
const bodyButton1 = document.createElement('div');
bodyButton1.setAttribute('class', 'body__button');
bodyButton1.setAttribute('id', 'yes');
bodyButton1.textContent = 'Yes';
bodyButtons.appendChild(bodyButton1);
const bodyButton = document.createElement('div');
bodyButton.setAttribute('class', 'body__button');
bodyButton.setAttribute('id', 'no');
bodyButton.textContent ='No';
bodyButtons.appendChild(bodyButton);
});
});
no.addEventListener('click', () => {
no.style.position = 'relative';
no.style.top = randomNum + 'px';
no.style.bottom = randomNum + 'px';
no.style.left = randomNum + 'px';
no.style.right = randomNum + 'px';
});
From what I understand, you want the button to keep moving down with each click. In that case, the callback currently keeps setting the distance from the top to 50px. All you need to do is have the function add 50 to the current value.
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<style>
button {
position: relative;
}
</style>
</head>
<body>
<button>Add new position</button>
<script>
const btn = document.querySelector('button');
btn.addEventListener('click', () => {
btn.style.top = ((parseInt(btn.style.top,10) || 0) + 50) + 'px';
});
</script>
</body>
</html>
Test it here
Things to note:
The value is saved as a string with a pixel suffix, i.e. "50px", so we'll use parseInt() to convert it to an int we can add to.
Before setting the style, AKA before the first click, its value is "" not 0, which doesn't work with parseInt(), hence the OR || operator.

Gamebook code throwing errors of undefined, content displayed at bottom of page and images wont load - Javascript

I keep getting all sorts of errors with this code. It is supposed to be a Gamebook engine code. A simple one. I am getting all sorts of things wrong with this and I do not know why.
My console keeps saying that line 1 () is undefined 404 not found.
Second, all my content keeps loading at the bottom of the page.
Third... An image should be loading on screen but is not. If I added a movie, it will play the movie, but not show the image.
Can you tell me what I am doing wrong?
EDIT: Fixed code to represent Solution:
<html>
<style>
objdc {
cursor: pointer;
text-decoration: underline;
color: blue;
}
</style>
<body>
<div>
<div id="StartRoomLoad" name="StartRoomLoad"></div>
<div id="StartRoomText"></div>
</div>
</body>
<script type="text/javascript" src="gamebook.js"></script>
<script>
var GameObjects = {
'titlescreen': [
['RoomName', 'Title Screen'],
['RoomDesc', 'Your first room Desc. Go to <objdc id="room2">Room 2</objdc>'],
['XRes', '320'],
['YRes', '240'],
['RmImg', 'http://selmiak.bplaced.net/games/c64/zak/room/74_intro_00_256.png'],
['RmMov', '']
],
'room2': [
['RoomName', 'Title Screen'],
['RoomDesc', 'Your first non-title screen room. Go back to the <objdc id="titlescreen">title screen room</objdc>'],
['XRes', '320'],
['YRes', '240'],
['RmImg', 'http://selmiak.bplaced.net/games/c64/zak/room/74_intro_00_256.png'],
['RmMov', '']
]
}
var GAMENAME = '';
var OBJECTGLOBAL = ''; //Indicates the current OBJECT loaded.
var GAMECURPLAYER = ''; //Indicates the current player you control.
var GAMESCORE = '0';
var GLOBALSETCLS = false;
const OBJECTNAME = 0;
const OBJECTDESC = 1;
const OBJECTXSIZE = 2;
const OBJECTYSIZE = 3;
const OBJECTIMAGE = 4;
const OBJECTMOVIE = 5;
function replaceAll(str, find, replace) {
return str.replace(new RegExp(find, 'g'), replace);
} //end function ParsePlayerInput
function LoadRoom(roomname) {
//Loads the specified room and sets the global setting. If a movie file is specified, the engine will play the movie file first. Once the movie is done, it will load the room image. Currently, the video will only play thru once. Once it is played, it will just show an image. If no movie is specified, it will load simply the room background image. If no image is present, it will show no image.
OBJECTGLOBAL = roomname;
if (GameObjects[OBJECTGLOBAL][OBJECTMOVIE][1] != '') {
document.getElementById("StartRoomLoad").innerHTML = '<video id="mainvid" onerror="hidevideo(OBJECTGLOBAL);" onended="hidevideo(OBJECTGLOBAL);" width="100%" height="" autoplay>' + '<source src="' + GameObjects[OBJECTGLOBAL][OBJECTMOVIE][1] + '" type="video/mp4"></video>';
} else if (GameObjects[OBJECTGLOBAL][OBJECTIMAGE][1] != '') {
//console.log(GameObjects[OBJECTGLOBAL][OBJECTIMAGE][1]);
document.getElementById("StartRoomLoad").innerHTML = "<img src='" + GameObjects[OBJECTGLOBAL][OBJECTIMAGE][1] + "' id='RoomBackground' width='" + GameObjects[OBJECTGLOBAL][OBJECTXSIZE][1] + "' height='" + GameObjects[OBJECTGLOBAL][OBJECTYSIZE][1] + "'></image>";
}
document.getElementById("StartRoomText").innerHTML = GameObjects[OBJECTGLOBAL][OBJECTDESC][1];
}
function hidevideo(roomname) {
//This function is called once a video is played on room enter. It will close the video and then show the image if there is one.
var x = document.getElementById("mainvid");
var y = document.getElementById("RoomBackground");
x.style.display = "none";
if (GameObjects[OBJECTGLOBAL][OBJECTIMAGE][1] != '') {
document.getElementById("StartRoomLoad").innerHTML = "<img src='" + GameObjects[OBJECTGLOBAL][OBJECTIMAGE][1] + "' id='RoomBackground' width='" + GameObjects[OBJECTGLOBAL][OBJECTXSIZE][1] + "' height='" + GameObjects[OBJECTGLOBAL][OBJECTYSIZE][1] + "'></image>";
}
}
LoadRoom('titlescreen');
var spans = document.getElementsByTagName('objdc');
for (i = 0; i < spans.length; i++)
spans[i].onclick = doRoomLoad;
function runSPANS() {
var spans = document.getElementsByTagName('objdc');
for (i = 0; i < spans.length; i++)
spans[i].onclick = doRoomLoad;
}
//document.getElementsByTagName('objdc').addEventListener('click',doRoomLoad,false);
function doRoomLoad() {
//var room = document.getElementById(this.id);
LoadRoom(this.id);
runSPANS();
}
</script>
</html>
The reason it works on IE and not Chrome is that your if statement is relying on a weird equality edge case. Take a look at this line:
if (GameObjects[OBJECTGLOBAL][OBJECTMOVIE][1] != ''){
GameObjects[OBJECTGLOBAL][OBJECTMOVIE] evalutes to the array ["RmMov"]. Therefore, GameObjects[OBJECTGLOBAL][OBJECTMOVIE][1] is undefined.
Comparing undefined to '' is a pretty weird equality check, so IE is interpreting this whole statement as false and Chrome as true.
Anyway, it's assuming that there will be a second value in your array, but there isn't. To fix it, change ['RmMov',] and ['RmMov'] to ['RmMov', ''].
That should work.

Changes to Javascript created element doesn't reflect to DOM

I have a class, that is supposed to display grey overlay over page and display text and loading gif. Code looks like this:
function LoadingIcon(imgSrc) {
var elem_loader = document.createElement('div'),
elem_messageSpan = document.createElement('span'),
loaderVisible = false;
elem_loader.style.position = 'absolute';
elem_loader.style.left = '0';
elem_loader.style.top = '0';
elem_loader.style.width = '100%';
elem_loader.style.height = '100%';
elem_loader.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
elem_loader.style.textAlign = 'center';
elem_loader.appendChild(elem_messageSpan);
elem_loader.innerHTML += '<br><img src="' + imgSrc + '">';
elem_messageSpan.style.backgroundColor = '#f00';
this.start = function () {
document.body.appendChild(elem_loader);
loaderVisible = true;
};
this.stop = function() {
if (loaderVisible) {
document.body.removeChild(elem_loader);
loaderVisible = false;
}
};
this.setText = function(text) {
elem_messageSpan.innerHTML = text;
};
this.getElems = function() {
return [elem_loader, elem_messageSpan];
};
}
Problem is, when I use setText method, it sets innerHTML of elem_messageSpan, but it doesn't reflect to the span, that was appended to elem_loader. You can use getElems method to find out what both elements contains.
Where is the problem? Because I don't see single reason why this shouldn't work.
EDIT:
I call it like this:
var xml = new CwgParser('cwg.geog.cz/cwg.xml'),
loader = new LoadingIcon('./ajax-loader.gif');
xml.ondataparse = function() {
loader.stop();
document.getElementById('cover').style.display = 'block';
};
loader.setText('Loading CWG list...');
loader.start();
xml.loadXML();
xml.loadXML() is function that usually takes 3 - 8 seconds to execute (based on download speed of client), thats why I'm displaying loading icon.

javascript - can't change an image's position

I'm trying to change a position of a image that I have in HTML by using javascript. I can actually get it working if I have the following code:
function main()
{
var catOne = document.getElementById("cat1");
catOne.style.left = cat1.getX().toString() + "px";
catOne.style.top = cat1.getY().toString() + "px";
}
but when I change the code to this:
var catOne = new Cat("cat1", 300, 100);
function main()
{
catOne.setUp();
}
it doesn't work. and I dont know why, but it only gives me an error of "TypeError: Cannot read property 'style' of null"
This is my Cat class in javascript:
function Cat(id, x, y)
{
this.cat = document.getElementById(id);
this.x = x;
this.y = y;
}
Cat.prototype.setUp = function ()
{
this.cat.style.left = this.x.toString() + "px";
this.cat.style.top = this.y.toString() + "px";
};
Cat.prototype.getX = function ()
{
return this.x;
};
Cat.prototype.getY = function ()
{
return this.y;
};
TypeError: Cannot read property 'style' of null means your catOne does not exist in the DOM tree.
You should instantiate the Cat class when the DOM is ready (or on window load).
I don't know why you need that main() function but when does it execute? It should also execute when the DOM is ready.
var catOne;
function main() {
catOne.setUp();
}
window.onload = function() {
catOne = new Cat("cat1", 300, 100);
main();
}
I also suggest that you set the position of your cat to absolute if you are positioning it like that in your setUp() function. (I think you are already doing this with your CSS):
Cat.prototype.setUp = function ()
{
this.cat.style.position = 'absolute';
this.cat.style.left = this.x.toString() + "px";
this.cat.style.top = this.y.toString() + "px";
};
Here is the fiddle.
Other than that your code should work.
with:
var catOne = new Cat("cat1", 300, 100);
catOne.setUp();
works just fine.
I don't quite get it how You managed to get error You mentioned
link: http://jsfiddle.net/Z74mM/

How to set CSS value with JavaScript?

I want to change the background color of an HTML element whose ID is foo. I have this code at present:
var hexcode = new Array('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f');
// this chooses a random value from the array hexcode
var ranval = function() {
return hexcode[Math.floor(Math.random()*hexcode.length)];
}
// six ranval() are put together to get color
var colorname = "#" + ranval() + ranval() + ranval() + ranval() + ranval() + ranval();
// trying to put the value on the background of element with "foo" ID.
document.getElementById("foo").style.color = colorname;
This code is throwing this error:
Uncaught TypeError: Cannot read property 'style' of null
I'm sure that ID foo exists.
Your error occurs because you're trying to access your element before the DOM is ready. Wait for the window to load before accessing it:
// Credit: http://paulirish.com/2009/random-hex-color-code-snippets/
function random_color() {
return '#' + ('00000' + (Math.random() * 16777216 << 0).toString(16)).substr(-6);
}
window.onload = function() {
document.getElementById("foo").style.backgroundColor = random_color();
};
Demo: http://jsfiddle.net/Blender/xQure/1/
The simple way to fix your code is:
var random_color = function() {
function randomHex() {
return Math.floor(Math.random() * 15).toString(16);
}
var str = '#';
for(var i = 6; i--;) {
str += randomHex();
}
return str;
}
window.onload = function() {
// For your body background color
document.body.style.backgroundColor = random_color();
// For your foo element TEXT color
document.getElementById("foo").style.color = random_color();
// For your foo element BACKGROUND color
document.getElementById("foo").style.backgroundColor = random_color();
};

Categories

Resources