Can I add/declare properties to a function in JavaScript? - javascript

I'm developing a RPG using HTML, CSS and JavaScript with jQuery as a personal project. Right now I'm doing the HUD where the player will have buttons to display information such as the Character Information, inventory, etc. Each of these buttons on will create a div where the info required will be displayed, if clicked again and the div is open it will delete it (or close it).
As far as I understand, functions in JS are treated as objects and thus properties can be added, I want to add a boolean property to the function that creates the div as a flag to check if the window is open or closed so I don't have to declare a global variable as flag to each button.
How do I declare those properties in a function?
Example:
let windowCharInfo = () => {
this.opened = false;
this.windowDisplay = function() {
$('<div>', {
class: 'HUDWindow',
id: 'charInfoWindow'
}).appendTo('#charInfo'); // Here's the window that will be created
// Some other code to add elements to that window will be here
}
}
The let windowCharInfo() is already an object or do I have to store it in a variable using 'new' keyword?
Also, windowCharInfo() will be called when the user clicks '#charInfo' (using onclick: 'windowCharInfo()')

Here is a Simple Constructor:
function Player(type) {
this.type = type;
this.weapons = []; // public
var thaco; // private
this.setTHACO = function(thaco) {
this.thaco = thaco;
return this;
}
this.getTHACO = function() {
return this.thaco;
}
this.addWeapon = function(weapon) {
this.weapons.push(weapon);
return this;
}
}
var player1 = new Player('elf'); // now it's an Object
player1.addWeapon('sword').addWeapon('axe').setTHACO('18');
console.log(player1.type);
var weapons1 = player1.weapons;
for (var i = 0, l = weapons1.length; i < l; i++) {
console.log(weapons1[i]);
}
console.log(player1.getTHACO());

Related

Javascript object function call wont work

I'm working on a HTML5 soundboard, but i've hit a bit of a snag...
I'm trying to get a stop function to work on all of the sounds at once that are played. Unfortunatly when i call this function from a buttonpress, the object doesn't appear to have a stop function. The code for the actual sound element is the following:
// Container to keep all sounds in one place. This is a Dictionary within a dictionary to be able to search by catagory.
var sounds = {};
// Counter to keep track of unique ID's
var lastID = 0;
// Base class for initializing Any class
var Base = function(methods){
var base = function() {
this.initialize.apply(this, arguments);
};
for (var property in methods) {
base.prototype[property] = methods[property];
}
if (!base.prototype.initialize) base.prototype.initialize = function(){};
return base;
};
//Complete class for the Sound object. Generates its own DIV's and HTML5 tags to play stuff.
var Sound = Base({
// Init all the variables.
initialize: function(name, file, target='Sounds') {
this.name = name;
this.file = file
this.button = null;
this.audioelement;
this.id = lastID + 1;
this.target = target;
lastID ++;
// Check if the catagory is there, if not: create it with a placeholder object
var catagory = sounds[this.target];
if(catagory == null){
sounds[this.target] = {99:null};
}
sounds[this.target][this.id] = this;
// Call init function
this.init();
},
play : function() {
obj = this
if(obj.audioelement.paused == true){
obj.audioelement.play();
}else{
obj.audioelement.pause();
obj.audioelement.fastSeek(0);
}
},
stop : function(){
obj = this;
obj.audioelement.pause();
},
init : function(){
// Statement for JS class based shenanigans.
obj = this
// Create a button and add some text to it
obj.button = document.createElement("BUTTON");
obj.button.appendChild(document.createTextNode(obj.name));
// Set ID's and names to keep track of this button
obj.button.id = obj.id;
obj.button.name = obj.target;
// Get or create parent element. Used for catagory based display
var el = getOrCreateElement(obj.target)
el.appendChild(obj.button);
// Create audio element and set appropriate settings
obj.audioelement = document.createElement("AUDIO");
obj.audioelement.src = obj.file;
obj.audioelement.name
obj.button.appendChild(obj.audioelement);
// Add function to play/pause to button
obj.button.onclick = buttonClicked;
});
function buttonClicked(){
// Fetch sound from dicionary container using the name and id from the button [SET AT SOUND.INIT()]
var sound = sounds[this.name][this.id];
// Call the play function in [SOUND]
sound.play();
}
And for the stopall function:
function stopAll(){
// Scroll through the entire dictionary
for ( var key in sounds){
for ( var id in sounds[key]){
// Check if the sound is not a placeholder
if(id == 99){
continue;
}
// Call stop function with fetched object.
var sound = sounds[key][id];
sound.stop();
}
}
}
The weird thing is is that the play function does seem to work, but not the stop function. It says that the object doesn't have that specific function...
Any ideas would be appriciated!
WM
for ( var x in object) loops over a few more properties than just methods, including ones on the base object's prototype.
If you console.log(id); within that inner loop you will see the extra ones.
Try adding this inside your inner for loop:
if (typeof id !== 'function') {
continue;
}
Edit: this isn't quite correct but I'm not in a position to see what it should be right now. It's almost there, from memory! Keep playing with it.

How to declare instance of a class within a instance of class?

I am making a simple hmtl/js game. I'd like to have all the data of the Game in DataofGame. It is like tennis, it is simpler than tennis: there is only set and match. changeinSet is called on click.
But I think i have a problem with private variable so it doesn't work.
Uncaught TypeError: Cannot read property 'WordsoftheGame' of undefined
//Added
document.getElementById('playboutton').addEventListener('click', newGame);
function newGame() {
var DataofGame = new newGameData();
}
// New game
function newGameData() {
this.pointTeam1 = 0;
this.pointTeam2 = 0;
this.WordsoftheGame = ShuffleListe();
this.ASet = new aSet();
}
//How the set is manage ********************
function aSet() {
var oneWord = DataofGame.ListeMot;
// display the word and delete it from the list
document.getElementById('jouer').innerHTML = oneWord[0];
DataofGame.WordsoftheGame.shift();
this.turn = true;
this.score = 0;
}
function changeinSet() {
DataofGame.ASet.score += 1;
//This is the other team's turn:
DataofGame.ASet.turn = !DataofGame.ASet.turn;
};
//shuffle liste
ListOfWords = ['Artiste', 'Appeler', 'Cheval', 'Choisir', 'Ciel', 'Croire', 'Dormir'];
function ShuffleListe() {
data = shuffle(ListOfWords);
return data;
}
function newGameData(){
this.pointTeam1=0;
this.pointTeam2=0;
this.WordsoftheGame= ShuffleListe();
this.ASet=new aSet();
}
//How the set is manage ********************
function aSet(){
var oneWord=DataofGame.ListeMot;
// display the word and delete it from the list
document.getElementById('jouer').innerHTML=oneWord[0];
DataofGame.WordsoftheGame.shift(); // << DataofGame not assigned yet
this.turn=true;
this.score=0;
}
Here when you're accessing DataofGame, it's not yet assigned because you're inside the constructor when calling aSet().
What you want to achieve is not completely clear, but if it's adding an ASet method to your object, you could write something like this:
function newGameData(){
this.pointTeam1=0;
this.pointTeam2=0;
this.WordsoftheGame= ShuffleListe();
this.ASet = function() {
// your code
};
}
NB your coding style for names is a bit messy, you should use uppercases consistently. The usage is to start constructor names with uppercases, the rest in lower cases.
You can let the function return an object with the data or just set the object.
function newGameData(){
return {
pointTeam1 : 0,
pointTeam2 : 0,
WordsoftheGame : ShuffleListe(),
ASet : new aSet()
}
}
But I would recommend to search for how to work with objects in javascript. Maybe this helps:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript

Javascript: How can I select a functions internal properties via it's parameters

I have a function that creates a menu, it has some buttons assigned to it and accepts an args parameter. Part of the function is a method called setActiveButton. When I create a menu I would like to dictate which of the buttons is active by passing in an option args.
For example:
var createMenu = function (args) {
this.btnOne = new Button(); // construct a button
this.btnTwo = new Button();
this.btnTwo = new Button()
this.setActiveButton(args.desiredButton);
return this;
}
createMenu({ desiredButton: btnTwo });
How do I tell createMenu to use one of it's buttons via args? I can't pass in { desiredButton: this.btnTwo } - because at that point this.btnTwo is not defined.
I was thinking about passing in a string and then using conditional statements like this:
var createMenu = function (args) {
var buttonChoice;
this.btnOne = new Button();
this.btnTwo = new Button();
this.btnThree = new Button();
if (args.desiredButton === "button one") {
this.setActiveButton(this.btnOne);
}
if (args.desiredButton === "button two") {
this.setActiveButton(this.btnTwo);
}
if (args.desiredButton === "button three") {
this.setActiveButton(this.btnThree);
}
return this;
}
createMenu({ desiredButton: "button two" });
However, I feel that there should be a cleaner and more succinct way to do this.
What is your suggestion?
just pass the name of the button as a string, and access with brackets.
var createMenu = function (args) {
this.btnOne = new Button(); // construct a button
this.btnTwo = new Button();
this.btnTwo = new Button()
this.setActiveButton(this[args.desiredButton]); // access this[property]
return this;
}
createMenu({ desiredButton: 'btnTwo' }); // string name of property
It's a little unclear to me why you are returning an object of properties that have no values, but you could do it like this. In my example I set the properties equal to strings like 'button 1', 'button 2', etc:
// pass number of total buttons, index of active button
var createMenu = function (total, active) {
var obj = {};
for (var i = 0; i < total; i++) {
obj['btn'+(i+1)] = 'button '+(i+1);
}
setActiveButton(obj['btn'+active]);
return obj;
}
In your example you reference setActiveButton as a property of the function but it isn't defined, so I have referenced it as a separate function.

How can I reuse an object literal

I'm trying to reuse an object I created to dynamically create more than one slider on a page.
My idea was to create an array and push my slider object there as often as needed, so I could access it by id. Unfortunatelly it doesn't work. Hope someone can point me in the right direction ...
So what I have is this;
var slider = {
"init":function(slide_it){
this.parent = $(slide_it);
/Count Elements and create a navigation depending on the count etc./
},
"otherstuff":{...}
}
In my (document).ready function I create an array and fill it up with different slider objects, add Ids to an accordion and call the init function:
var slide_array = [];
var accordion_sections = $('#accordion > div').length;
for(var i = 0; i < accordion_sections; i++){
slide_array.push(slider);
$('#accordion').children('div').eq(i).attr('id', 'slide_it_'+ i);
slide_array[i].init($('#slide_it_' + i).find('.slider'));
}
Then I have a button with class="next" and I call a function within the slider
$('.next').click(function(){
slide_array[0].otherstuff();
});
My plan is to get the parent of .next and its id so that I can use slide_array[parentID].otherstuff();
But ... it's not working propperly when I call the init function inside the for loop more then once.
More weird, some functions calls seem to work, other have no effect.
What am I doing wrong?
You can use Object.create.
var s1 = Object.create(slider),
s2 = Object.create(slider);
s1.init(...);
s2.init(...);
If you return this from init your will be able to chain like:
var s1 = Object.create(slider).init(...);
However at this point I would just ditch the object literal and use constructors, since this is what you need.
function Slider(slide_it) {
this.parent = $(slide_it);
}
Slider.prototype = {
constructor: Slider,
otherStuff: function () {}
};
var s1 = new Slider(...),
s2 = new Slider(...);
Write a function to return the object:
function slider() {
return {
"init":function(slide_it){
this.parent = $(slide_it);
/Count Elements and create a navigation depending on the count etc./
},
"otherstuff":{...}
};
}
Then:
slide_array.push( slider() );
That'll give you a separate object every time. In you're version, you're filling the array with references to the same single object.
Why not just turn that into a jQuery plugin ?
jQuery.fn.slider = function(options) {
return this.each(function() {
var sliderElem = $(this),
settings = $.extend({
speed : 3000,
something : 'other thing'
}, options);
otherStuff(sliderElem);
});
function otherStuff(elem) {
}
}
$('#accordion > div').slider();
No iteration or jumping through hoops, just call it on the collection and it creates a new slider for each element ?

Custom object 'game' doesn't have specified method

I'm trying to call a method cardSelection() from a function game() but instead I'm getting an error report which throws back to me the whole function with a "has no method cardSelection()" The idea is to access the method through the click of a button, which HTML tag is as follows:
<img id="PlayerCard0" class="card" src="images/Cards/Mario.png" alt="Mario" title="Mario" onclick="game.cardSelection('PlayerCard0')">
I'm not posting the whole Javascript as I believe this to be the case of a mere declaration error, anyhow, game() and cardSelection() were declared as follows:
function game()
{
...
this.cardSelection = function(card)
{
var cardElem = document.getElementById(card);
var id = cardElem.getAttribute("id");
var call = document.getElementById("call");
var select = function(card)
{
var found = 0;
for (var card = 0, totalCards = 5; card < totalCards; card++)
{
if (document.getElementById("PlayerCard" + card + "selected"))
{found++}
}
if (found == 0)
{
call.setAttribute("onclick", "changeHand()");
call.childNodes[0].nodeValue = "Change";
}
if (found < 3)
{
id += "selected"
setAttributes(cardElem,
{
"id" : id,
"style": "position: relative; top: 1em;",
"onclick" : "cardSelection('" + id + "')"
});
}
else { return; }
}
var unselect = function (card)
{
cardElem.removeAttribute("style");
id = id.replace("selected","");
setAttributes(cardElem,
{
"id" : id,
"onclick" : "cardSelection('" + id + "')"
});
var cardNumber = 0;
var found = false;
while (cardNumber < 5 && !found)
{
if (document.getElementById("playerCard" + cardNumber + "selected"))
{found = true;}
cardNumber++;
}
if (!found)
{
call.setAttribute("onclick", "compareHands()");
call.childNodes[0].nodeValue = "Hold";
}
}
if (id.indexOf("selected") >= 0){unselect(card);}
else {select(card);}
}
...
}
How game() is called:
window.onload = function openingScreen()
{
var startGame = document.createElement("a");
startGame.setAttribute("onclick", "game()");
startGame.appendChild(document.createTextNode("Play"));
window.table = document.getElementById("table");
table.appendChild(startGame);
}
The problem you are experiencing is the result of confusion about Objects/Classes/Instances in javascript.
The critical point for you on this issue is the difference between new game() and game();
var foo = new game()
tells the JS engine to create a new object
point that object's Prototype (not prototype) at game's prototype
and then invoke the function game, but for the sake of the body of that function this will refer to the created object.
If the function doesn't return an object, assign our created object to foo (otherwise assign the function's return value to foo
Inside the body of your game function, you have this.cardSelection = function (....
If you simply invoke game as a function, so just game(), without the new keyword, this inside the body of the function will be the window object! So you'll add cardSelection to the window object.
Also importantly: game.cardSelection() is looking for a function named cardSelection as a property on the function game.
Here's an example of using that style that would work:
var foo = function () {
//do interesting stuff
}
foo.bar = function () {
//do interesting stuff related to foo
}
foo.bar();
What you seem to be expecting would need to be written this way:
var game = function () {
this.cardSelection = function () {
//perform card selection!
}
}
var aGame = new game();
aGame.cardSelection();
Or, if cardSelection does not need access to any private properties of the game, it could be written more efficiently as
var game = function () {
//setup the game
};
game.prototype.cardSelection = function () {
//perform card selection
};
var aGame = new game();
aGame.cardSelection();

Categories