I am very very new to vanilla JS and I am currently working on an assignment that allows us freedom to create whatever kind of website we want on the basis that we break all of the code down into functions and that we do not use global variables.
I have almost completed a very basic address book that takes a name and phone number as input, stores the values into an array and returns the list of contacts when the 'view all contacts' button is pressed. My only dilemma is that the array is a global variable and I am not sure how to get this to work other wise.
Update
I have added the entire code here. I am working in Brackets.
function init(){
document.getElementById("addButton").addEventListener("click", addContact)
document.getElementById("listButton").addEventListener("click", listContacts)
}
var contactList = [];
function getAttribute(id){
return document.getElementById(id).value;
}
function verifyNumber(number){
var regExp = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/;
var phone = number.match(regExp);
if (phone) {
outPut("alert","");
return true;
}else{
outPut("alert","invalid phone number");
return false;
}
}
function outPut(id,message){
var messageBox = document.getElementById(id);
messageBox.innerText = message;
}
function contact(name, number){
this.name = name;
this.number = number;
return ("Name: " + this.name + "Number: " + this.number);
}
function addContact(){
var name = getAttribute("name");
var number = getAttribute("number");
if(verifyNumber(number)){
var newContact = new contact(name,number);
contactList.push(newContact);
alert("Contact Successfuly Added!");
}
}
function listContacts(){
contactList.forEach(element => console.log(element));
contactList.forEach(Object => {var newElement = document.createElement('h1');
newElement.innerHTML = JSON.stringify(Object);
document.body.appendChild(newElement);
});
}```
Maybe use an object instead? like key: value pairs, and could you share a snippet of your code with us so we can help you better, thanks
Related
When I try to grab the object from the array, the type is undefined. Therefore I cannot use a method from the undefined object as it doesn't exist. I am relatively new to JavaScript and I have come straight from Java so the way of retrieving objects is kind of new to me. This is what I currently have.
var fleetAmount = 0;
var fleets = [];
function Fleet(number) {
this.number = number;
this.activities = [];
this.addActivity = function (activity) {
this.activities.push(activity);
};
fleets.push(this);
}
var getFleet = function(fleetNumber) {
return fleets[fleetAmount - fleetNumber];
}
This is where I try to grab the object and preform the function
const Fl = require(‘fleet.js’);
const fleet = Fl.getFleet(fleetNumber);
fleet.addActivity(activity);
I am also working in Node.js, which is how I am using the require method.
In combination with the answer from #audzzy I changed the getFleet() function so that it would be more efficient. I tested it out and it worked. This is what I used
function getFleet(fleetNumber) {
let result = fleets.filter(function (e) {
return e.number = fleetNumber;
})
return result[0];
}
Thanks for the help! I appreciate it.
you want to create a new fleet object and add it, not "this"..
adding "this" would cause a circular reference, where
this.fleets[i] = this (and all fleets would have the same value)
when calling get fleet, I would check that a fleet was returned from get fleet
in case amount is less than the number you send to getFleet (where according to what you posted: 1 returns the last, 2 returns second to last etc..)..
I hope this explanation makes sense.. anyways, this should work..
var fleets = [];
doStuff();
function doStuff(){
addFleet(1);
addFleet(2);
addFleet(7);
addFleet(3);
// should return null
let fleet1 = getFleetByNumber(5);
// should return the fleet with number 7, and not change the fleet with number 1
let fleet2 = getFleetByNumber(7);
if(fleet2){
fleet2.addActivity("activity");
}
console.log(`fleets: ${JSON.stringify(fleets)} \nfleet1: ${JSON.stringify(fleet1)} \nfleet2: ${JSON.stringify(fleet2)}`);
}
function addFleet(number) {
let fleet = { number: number,
activities: [] };
fleet.addActivity = function (activity) {
this.activities.push(activity);
};
fleets.push(fleet);
}
function getFleetByNumber(fleetNumber) {
return fleets.find(function (e) {
return e.number == fleetNumber;
});
}
function getFleet(fleetNumber) {
let result = null;
if(fleets.length - fleetNumber >= 0){
result = fleets[fleets.length - fleetNumber];
}
return result;
}
I am trying to create a Blackjack game and am having an issue when it comes to splitting the hands. Ultimately I want to create an associative array that has a total and a status for each split, treating each one as it's own hand object. One of the issues I'm having is with the naming functionality which I am trying to do dynamically so each time the Split Function is called, it creates the name according to the number of times the hand has been split.
A bit of background on what I have created so far; A card object holds the name, suit and value (ie. Queen, Clubs, 10). I have an array called cardsInDeck that hold all the card objects in a deck which is shuffled so each card can be pulled randomly. When a card is pulled from the deck, the value is pushed into an array for calculating the value, and the name and suit are concatenated and added to a string to populate the HTML to show the cards on the page.
function drawOne() {
let card = cardsInDeck.pop();
return card;
}
var handNumber = 0;
var playerHand01 = [];
var p1, p2, d1;
function initialDealOut() {
++handNumber;
p1 = drawOne();
playerHand01.push(p1.value);
++playerCount;
let p1n = p1.name + p1.suit;
let p1c = "images/" + p1n + ".png";
let p1Image = document.getElementById('player01');
p1Image.src = p1c;
}
This is repeated for the dealers first card (d1) and the Player's second card (p2) and works well and good. What I would like to do is have the playerHand01 be part of a Hand object that is in a Player array that holds all the hands, and associate a status ("action", "stand", "bust") to each Hand object.
I am trying to accomplish this with something like the code below:
var player = new Array();
function Hand(total, status) {
this.total = total;
this.status = status;
}
function Split() {
++handNumber;
let handName = "playerHand0" + handNumber;
let handTotal = "playerHandTotal0" + handNumber;
handName = new Hand {
handTotal: 0,
status: "action"
}
}
I am new to programming and I know it can be done, just know that the way I am approaching it isn't quite right.
Any recommendations would be appreciated.
I would probably just use an array to store the hands. They'll be accessible by their index. If you want to give them a name, that could be part of the hand object.
let playerHands = [];
function Split() {
++handNumber;
let newHand = new Hand ({
handName: 'playerHand' + handNumber
handTotal: 0,
status: 'action'
});
playerHands.push(newHand);
}
Alternatively, if you want to use an object (perhaps because i've misunderstood your requirements), it is possible to give an object a property dynamically. There are two different syntaxes that can be used for object properties. Both of the following have the same result:
let myObject = {};
myObject.test = 'hi';
and
let myObject = {};
myObject['test'] = 'hi';
You'll usually use the former, but if you don't know the name until runtime, the latter gives you increased flexibility, because you can access using any string. So for example, the following is perfectly valid:
let player = {};
let handName = 'playerHand0' + handnumber;
player[handName] = new Hand();
This is a little confusing to read and I don't think you've fully thought through what information you need to store.
Since the focus of this question is really how to split a hand IMO, I will show you that in hopes that it will point you in the right direction.
First the hand object. I will use es6, but you can do something similar using the constructor method that you have there.
class Hand {
constructor(total, status){
this.total = total;
this.status = action;
this.cards = [];
}
drawCard(){
this.addCard(drawOne());
}
addCard(card){
this.cards.push(card);
this.updateScore();
}
updateScore(){
// loop through and update the score and status
}
// this is the one you've been waiting for
split(player){
// can only split in certain conditions
if(this.cards.length !== 2 || this.cards[0] !== this.cards[1]){
console.log('cannot split this (?)');
return null;
}
// create the new hand
let additionalHand = new Hand(0, 'active');
additionalHand.addCard(this.cards.pop());
player.hands.push(additionalHand);
// need to draw a new card for each hand
this.drawCard();
additionalHand.drawCard()
//return the new hand in case you want to do something with that.
return additionalHand;
}
}
This may not be exactly what you want, but it should be a good initial guide to help.
I would suggest classes for each of the concepts (Player, Card, Deck, Hand, Game), and at the Game level you would keep an array of active hands, which (after a split), could have a different number than the number of players.
Here is how it could look:
class Card {
constructor(name, suit, value) {
this.name = name+'';
this.suit = suit;
this.value = typeof name === 'number' ? name
: 10 + (name === 'Ace');
}
}
class Player {
constructor(name) {
this.name = name;
}
}
class Hand {
constructor(player, total, status) {
this.player = player; // a back reference to the owning player
this.total = total;
this.status = status;
}
addCard(card) {
this.total += card.value;
}
copy() {
return new Hand(this.player, this.total, this.status);
}
}
class Deck {
constructor() {
this.cards = [];
for (let suit of ['Clubs', 'Hearts', 'Spades', 'Diamonds']) {
for (let name of ['Ace',2,3,4,5,6,7,8,9,10,'Jack','Queen','King']) {
this.cards.push(new Card(name, suit));
}
}
}
shuffle() {
for (let i = this.cards.length; i; i--) {
let j = Math.floor(Math.random() * i);
[this.cards[i - 1], this.cards[j]] = [this.cards[j], this.cards[i - 1]];
}
return this;
}
drawOne() {
return this.cards.pop();
}
}
class Game {
constructor() {
this.players = [];
this.activeHands = [];
this.currentHandId = 0;
this.deck = new Deck().shuffle();
}
addPlayer(player) {
this.players.push(player);
// Create a hand for this player:
this.activeHands.push(new Hand(player, 0, "active"));
}
deal() {
// Draw card and assign to current hand
this.activeHands[this.currentHandId].addCard(this.deck.drawOne());
}
nextHand() {
this.currentHandId = (this.currentHandId + 1) % this.activeHands.length;
}
split() {
// Insert the new, copied hand just after the current one:
this.activeHands.splice(this.currentHandId+1, 0,
this.activeHands[this.currentHandId].copy());
}
bust() {
// remove hand from active hands:
this.activeHands[this.currentHandId].status = "busted";
this.activeHands.splice(this.currentHandId, 1);
}
}
// Start a game
var game = new Game();
game.addPlayer(new Player('Helen'));
game.addPlayer(new Player('John'));
// Deal a card to the first active hand
game.deal();
game.nextHand(); // move to second player
game.deal();
// ...etc.
// When you need to split the current hand
game.split();
// Then deal a card to both hands:
game.deal();
game.nextHand(); // move to second hand of same player
game.deal();
// ..etc.
You would of course need to add more game logic and rules. But this could be a template to extend to your needs.
The basic idea is to check if it starts with an underscore and if there is split the string and return whatever comes after the underscore. This function will be run many times, but for different strings, it is unlikely i will need to retrieve the information more than once for each stirng.
A simple function which will return an object with the data I need:
var parseElementName = function(i) {
var sliced = [i.slice(0, 1), i.slice(1, i.length)];
var obj = {
isClass: null,
name: ''
}
if(sliced[0] === '_') {
obj.name = sliced[1];
obj.isClass = true;
} else {
obj.name = i;
obj.isClass = false;
}
return obj
}
Called with parseElementName(i);
Object with prototyped function
var parsedElement = function(i) {
this.className =
this.isClass = null;
if(this.setElementName(i))
return true
}
parsedElement.prototype.setElementName = function(i) {
var sliced = [i.slice(0, 1), i.slice(1, i.length)];
if(sliced[0] === '_') {
this.className = sliced[1];
this.isClass = true
} else {
this.className = i;
this.isClass = false
}
}
Called with var parsed_element = new parsedElement();
then parsed_element.className or parsedElement.isClass
Which approach is recommended?
I like the object prototype approach best, but I have a few notes about your code:
Use semicolons at the end of each line
Class names should be capitalized. So it should be ParsedElement
I wouldn't call it className, because it is confusing when it is not a class, I would rename it name
The two ways have different outcomes - that constructor+prototype approach will yield an instance which has a setElementName method. Will you ever need this to change the fields of an existing object? It's a simple parser function, so I would assume no. In that case, you should go with returning the object literal:
function parseElementName(i) {
var isClass = i.charAt(0) == '_';
return {
isClass: isClass,
name = isClass ? i.slice(1) : i
};
}
If you really need that method later, consider #MaxMeier's and #HMR's points.
Updated jsFiddle: http://jsfiddle.net/leongaban/NmH97/
Inside my main function is a button state modifier function and several click functions.
Having a bit of trouble updating the boolean values in the parent function. I could solve this by using global variables, but I know I shouldn't need to do that here.
In the function below, when #toggle_company is clicked I pass the bool company (which is set to true by default) into the setupToggleButtons function. The current state is then switched, but the original company bool value is unchanged, how would you write this to target and update the variable company in the parent function wireSearchIcons?
var wireSearchIcons = function() {
// Boolean Flags
var company = true;
var phone = true;
var orange_on = '#F37B21'; var orange_off = '#f3cdb1';
var green_on = '#3DB54A'; var green_off = '#d8e0c3';
// Other code...
$("#toggle_company").unbind('click').bind('click', function () {
setupToggleButtons(company, '#toggle_company path', orange_on, orange_off);
});
function setupToggleButtons(bool, path, on, off) {
var path = $(path);
if (bool) {
path.css('fill', off);
bool = false;
return bool;
} else {
path.css('fill', on);
bool = true;
return bool;
}
console.log(bool);
}
}
When you use the variable in the function call, you will be sending the value that the variable contains, not the variable itself. Changing the parameter has no effect on the variable where the value came from.
Javascript doesn't support sending parameter by reference, so just return the value from the function, and assign it back to the variable:
$("#toggle_company").unbind('click').bind('click', function () {
company = setupToggleButtons(company, '#toggle_company path', orange_on, orange_off);
});
function setupToggleButtons(bool, path, on, off) {
var path = $(path);
if (bool) {
path.css('fill', off);
bool = false;
} else {
path.css('fill', on);
bool = true;
}
return bool;
}
It doesn't make sense to write a setupToggleButtons function that does so little. You should inline the code into the click handlers, or create the click handlers within the setup function.
var wireSearchIcons = function() {
// Flags
var company = true;
var phone = true;
var orange_on = '#F37B21'; var orange_off = '#f3cdb1';
var green_on = '#3DB54A'; var green_off = '#d8e0c3';
// Toggle Button States
$("#toggle_company").unbind('click').bind('click', function () {
company = !company;
$('#div_company').css('background', company ? orange_on : orange_off);
});
$("#toggle_phone").unbind('click').bind('click', function () {
phone = !phone;
$('#div_phone').css('background', phone ? green_on : green_off);
});
}
wireSearchIcons();
I have an assignment in which I need to make two arrays (NAME and SALES). I need to populate the array of up to 100 components. Then, I need to calculate gross pay using a calcPay() function. I am having trouble figuring out how to get the function to work, it either prints the resulting table with the Pay column as 'undeclared', or it just stops working when it comes to that spot, no matter how many NAMES and SALES are entered into the array. I have this in the body script:
var i=0;
var NAME = new Array();
var SALES = new Array();
do
{
NAME[i]=getName();
SALES[i]=getSales();
i++;
var again=confirm("Would you like to enter another salesperson's stats?");
}while(again && i<=100);
var i=0;
for (var i=0;i<NAME.length;i++)
{
printRow();
}
And this is the header:
function getName()
{
do
{
var name=prompt("What is the salesperson's full name?");
}while (name==""||name==null);
return name;
}
function getSales()
{
do
{
var sales=prompt("Please enter salesperson's sales.");
}while(sales==""||isNaN(sales));
return parseFloat(sales);
}
calcPay(sales)
{
var pay=sales*.1+1000;
return pay;
}
function printRow()
{
document.write("<tr>");
document.write("<td>"+NAME[i]+"</td>");
document.write("<td>"+SALES[i]+"</td>");
var payment=calcPay(SALES[i]);
document.write("<td>"+payment+"</td>");
document.write("</tr>");
}
This is not the full extent of the assignment by any means, I just want to make sure that I have a handle on the feeding and manipulating of the arrays (which I don't, obviously).
Thanks for any tips.
Generally - your code works, find it here:
http://jsfiddle.net/osher/GhZSf/
However -
there is a missing "function" before calcPay
calcPay(sales)
{
var pay=sales*.1+1000;
return pay;
}
should be
function calcPay(sales)
{
var pay=sales*.1+1000;
return pay;
}
that's all
name and sales are out of scope this function will not do what you think it will and even if it does it is wrong.
Use an if statement .
function getName()
{
do
{
var name=prompt("What is the salesperson's full name?");
}while (name==""||name==null);
return name;
}
function getSales()
{
do
{
var sales=prompt("Please enter salesperson's sales.");
}while(sales==""||isNaN(sales));
return parseFloat(sales);
}