My Intent: Making a CYOA Using Objects
I'm quite new to Javascript and have been trying to make a simple CYOA game, using this code from this Reddit comment as a template. However, I want to use objects (whose values are intended to be constant) to store the various string values for the messages and the object each choice points to, as opposed to having all the objects in arrays and having to point to them using using their index in the array. My reasoning is that it'd be (theoretically) simpler for me to organize using strings like "msg_001" or "story5_28" rather than having to change a bunch of numbers in the event I inserted some new set of messages in the middle of an array.
My Problem: The First Message Isn't Displayed Again
Basically, I want to loop back to the first message and its set of answers, but it won't.
The initial printCurrentMsg() works (changing the content of the "message" divs to the values of msgText, and looping through the object's choices array to set the buttons in the "choices" div, based on the object specified in currentMsg) and the buttons' respective onlick attributes seem to work, until they're set to show msg_000.
It appears to be that whatever the value of currentMsg is, printCurrentMsg won't show the object the string refers to, other than when it does initially. Additionally, after using console.log at various points in the script, I noticed that currentMsg isn't changed, and using console.log(typeof) for both currentMsg and window[currentMsg] shows that the former is a string and the latter is an object. Am I unintentionally creating two separate variables?
I've tried...
...using parameters in printCurrentMessage.
...using currentMsg in the functions instead of window[currentMsg].
...using dot notation instead of bracket notation.
...using this[] instead of window[].
I'm not sure whether this has to do with asynchronicity, I'm accessing object properties incorrectly, my comprehension of scope is flawed, or if I'm erroneously using global variables. Should I be using some sort of callback?
Using a "dummy" msg_000—making another object with a different name but the same properties—serves as a stopgap solution, but I still wouldn't understand what the problem is. Having all the msg_*** objects in an array and referring to them by index number instead of string would also work, but I'm hesitant to rely on that for both the aforementioned tediousness and the fact that I still don't understand why the value of currentMsg remains unchanged.
In order to better articulate my problem, here is a jsfiddle with my code, and I shall post it below as well.
//messages
var msg_000 = { //Starts with this one, I want to be able to go back to it
msgName: "msg_000",
msgText: "Sup! Choose an option!",
choices: [
ans_000 = {
ansText: "Climb a hill!",
ansGoto: "msg_001" //this works
},
ans_001 = {
ansText: "Skin a cat!",
ansGoto: "msg_002" //this works
},
ans_002 = {
ansText: "Build a birdhouse!",
ansGoto: "msg_003" //this works
}
]
};
var msg_001 = {
msgName: "msg_001",
msgText: "You summit the great snowy peaks!",
choices: [
ans_000 = {
ansText: "Talk to the Recursion Guru!",
ansGoto: "msg_000" //this doesn't work
}
]
};
var msg_002 = {
msgName: "msg_002",
msgText: "You suffer severe lacerations to the face!",
choices: [
ans_000 = {
ansText: "Start Over",
ansGoto: "msg_000" //this doesn't work
}
]
};
var msg_003 = {
msgText: "You build a pretty average looking birdhouse. Some grackles have moved in nonetheless, placing their various knicknacks, bedding materials, and chrono-gateways within their new abode.",
choices: [
ans_000 = {
ansText: "Step through the chrono-gateway!",
ansGoto: "msg_000" //this doesn't work
},
ans_001 = {
ansText: "I think I wanna climb that mountain over there.",
ansGoto: "msg_001" //this works
}
]
}
var currentMsg = "msg_000"; //the first message is "msg_000"
printCurrentMsg = function() {
document.getElementById("message").innerHTML =
window[currentMsg].msgText;
//sets the message (the div with the id of "message")
//based on the "currentMsg" variable. "currentMsg.msgText"
//doesn't seem to work.
var choices = "";
for (var i = 0, l = window[currentMsg].choices.length; i < l; i++) {
choices += "<p><button onclick='setMessage(" +
window[currentMsg].choices[i].ansGoto + ")'>" +
window[currentMsg].choices[i].ansText + "<br>Goto " +
window[currentMsg].choices[i].ansGoto + "</button></p>";
//make the buttons, sets the button's onclick
//"setMessage" function's parameter to the the value of
//the "ansGoto" property -> in the answers object at the
//i/th index of the choices property array -> in the
//"msg_(number)" object."
};
document.getElementById("choices").innerHTML = choices;
//takes the value of the "choices" [local?] variable and puts
//it in the "choices" div.
};
setMessage = function(msg) {
window[currentMsg] = msg; //I think this is the source of all
//my problems, it's supposed to set "currentMsg" to the value
//of the "msg" parameter, but when I check it with
//console.log(currentMsg) it hasn't been changed (i.e., still
//it's initial value of "msg_000") and when I use
//console.log(window[currentMsg]) it returns "[Object
//object]"; using typeof shows me that "currentMsg" is a
//string and "window[currentMsg]" is an object. I thought
//they both were the same object, am I unintentionally
//creating two different objects?
printCurrentMsg(); //runs that function, seems to display the
//messages except the ones from object "msg_000".
};
printCurrentMsg(); //Displays the initial message and choices
//from "msg_000", but after a new message is chosen it won't
//display "msg_000" if it's pointed to from an "ansGoto"
//property.
<!DOCTYPE html>
<html>
<body>
<div id="message"></div>
<!-- "msgText" goes here -->
<div id="choices"></div>
<!-- "choices" go here -->
</body>
</html>
Thank you for your time.
When setMessage() does window[currentMsg] = msg;, it replaces the value of the variable holding a message. E.g. if the current message is msg_000, and you do setMessage(msg_002), it's equivalent to writing msg_000 = msg_002;.
What you really want to do is change the value of currentMsg to be the name of the new message. So you should do: currentMsg = msg.msgName;.
You were also missing the msgName property in msg_003.
As a best practice, you shouldn't use global variables for all this. Create your own object messages, and use messages[currentMsg].
//messages
var msg_000 = { //Starts with this one, I want to be able to go back to it
msgName: "msg_000",
msgText: "Sup! Choose an option!",
choices: [
ans_000 = {
ansText: "Climb a hill!",
ansGoto: "msg_001" //this works
},
ans_001 = {
ansText: "Skin a cat!",
ansGoto: "msg_002" //this works
},
ans_002 = {
ansText: "Build a birdhouse!",
ansGoto: "msg_003" //this works
}
]
};
var msg_001 = {
msgName: "msg_001",
msgText: "You summit the great snowy peaks!",
choices: [
ans_000 = {
ansText: "Talk to the Recursion Guru!",
ansGoto: "msg_000" //this doesn't work
}
]
};
var msg_002 = {
msgName: "msg_002",
msgText: "You suffer severe lacerations to the face!",
choices: [
ans_000 = {
ansText: "Start Over",
ansGoto: "msg_000" //this doesn't work
}
]
};
var msg_003 = {
msgName: "msg_003",
msgText: "You build a pretty average looking birdhouse. Some grackles have moved in nonetheless, placing their various knicknacks, bedding materials, and chrono-gateways within their new abode.",
choices: [
ans_000 = {
ansText: "Step through the chrono-gateway!",
ansGoto: "msg_000" //this doesn't work
},
ans_001 = {
ansText: "I think I wanna climb that mountain over there.",
ansGoto: "msg_001" //this works
}
]
}
var currentMsg = "msg_000"; //the first message is "msg_000"
printCurrentMsg = function() {
document.getElementById("message").innerHTML =
window[currentMsg].msgText;
//sets the message (the div with the id of "message")
//based on the "currentMsg" variable. "currentMsg.msgText"
//doesn't seem to work.
var choices = "";
for (var i = 0, l = window[currentMsg].choices.length; i < l; i++) {
choices += "<p><button onclick='setMessage(" +
window[currentMsg].choices[i].ansGoto + ")'>" +
window[currentMsg].choices[i].ansText + "<br>Goto " +
window[currentMsg].choices[i].ansGoto + "</button></p>";
//make the buttons, sets the button's onclick
//"setMessage" function's parameter to the the value of
//the "ansGoto" property -> in the answers object at the
//i/th index of the choices property array -> in the
//"msg_(number)" object."
};
document.getElementById("choices").innerHTML = choices;
//takes the value of the "choices" [local?] variable and puts
//it in the "choices" div.
};
setMessage = function(msg) {
currentMsg = msg.msgName;
printCurrentMsg(); //runs that function, seems to display the
//messages except the ones from object "msg_000".
};
printCurrentMsg(); //Displays the initial message and choices
//from "msg_000", but after a new message is chosen it won't
//display "msg_000" if it's pointed to from an "ansGoto"
//property.
<!DOCTYPE html>
<html>
<body>
<div id="message"></div>
<!-- "msgText" goes here -->
<div id="choices"></div>
<!-- "choices" go here -->
</body>
</html>
Related
I have been looking for the answer to this problem, but I haven't found exactly a similar issue.
I have created several objects (ob0, ob1, ob2) they all contain the same entries.. but on each object the value of the entries is different. i.e. ob0.brand has one value but oj1.brand has a different value.
I want to visualize the info of the objs on a website. there is a radio button that returns ob0, ob1, or ob2 accordingly to what is chosen.. and on one div class, the text content of the value of the entries is to be displayed.
the radio buttons value is named a var objSelc and try to use documentselctor to display ojbSelc.brand so that whatever the user selects would represent obj0.brand or obj1.brand etc. However, this returns an error.
Maybe it's not possible to use a var as the name of an object? or what workaround would there me??
btw I am a level 0.1 first month of learning, Any help will be appreciated.
here is the snippet of code in question:
so here is for example an inventory of a bike and the different components, brake pads, tires, chain etc. I have made different copies of these but replaced the content info.
let UA = {
brakes: "shimano b10s",
backTire: " Bigben 26",
frontTire: "bigben 20",
bikeChain: " 1/32 single speed",
motorBrand: " Bosch Intuvia, cargoline",
};
let cargoCarla = {
breaks: "shimano b10s",
backTire: "big apple 24",
frontTire: "big apple 20",
bikeChain: "na",
motorBrand: "na",
};
Here comes some user input from the radio buttons from the HTML site
var form = document.querySelector("form");
var log = document.querySelector("#log");
form.addEventListener(
"submit",
function (event) {
var data = new FormData(form);
var output = "";
for (const entry of data) {
output = output + entry[0] + "=" + entry[1] + "\r";
}
// log.innerText = output;
event.preventDefault();
var splitOut = output.split("=");
var bikeSelc = splitOut[1].toString();
showComponent.textContent = bikeSelc.brakes;
},
false
);
In fact, bikeSelc does produce either UA or cargoCarla depending on which was selected. but bikeSelc.brakes do not produce a result so JS is not reading the value of bikeSelc when added to.brakes. even though consel.log does give the proper variable.
showComponent.textContent = UA.brakes;
This was just me hardcodingto see what it would look like on the DOM
I was learning to edit an array with a function and came across these two similar functions. I was wondering what is the difference between them. They both worked the same for me, but are there more pros or cons to using one of them?
function edit(position, newTodoText){
userTodos[position] = ({todoText: newTodoText});
console.log(userTodos);
}
and
function edit(position, newTodoText){
userTodos[position].todoText = newTodoText;
console.log(userTodos);
}
One difference is that the second option requires userTodos[position] object to already exists and would cause an exception if it doesn't.
Uncaught TypeError: Cannot set property 'todoText' of undefined
Another difference is that the second option performs mutation (which means it edits an existing todo) while the first option creates a new todo at the same position (and the old one is deleted via something called garbage collection)
Here's some code with commentary to illustrate the differences:
function optionOneInserts(x) {
x[0] = { name: "alice" };
}
function optionTwoMutates(x) {
x[0].name = "alice";
}
let x1 = [{ name: "bob" }];
let x2 = [{ name: "bob" }];
let previousFirstItemInX1 = x1[0];
let previousFirstItemInX2 = x2[0];
console.log("Current first item in x1 and x2:");
console.log(`current x1[0] = ${JSON.stringify(previousFirstItemInX1)}`);
console.log(`current x2[0] = ${JSON.stringify(previousFirstItemInX2)}`);
console.log(`\n...performing options one and two`);
optionOneInserts(x1);
optionTwoMutates(x2);
console.log("\nboth options end up with same values in x1 and x2:");
console.log(`x1 = ${JSON.stringify(x1)}`); // [{ name: 'alice' }]
console.log(`x2 = ${JSON.stringify(x2)}`); // [{ name: 'alice' }]
console.log(
"\nbut the first option leaves its old first value untact and adds a new value, while the second option changes the old value instead:"
);
console.log(`previous current x1[0] = ${JSON.stringify(previousFirstItemInX1)}`);
console.log(`previous current x2[0] = ${JSON.stringify(previousFirstItemInX2)}`);
The first form will create a new object at the given position with only one property while the second form will extend an already existing object at that position with the given property and contents.
For the second form to be a bit more "tolerant" you could modify it to
function edit(position, newTodoText){
(userTodos[position]=userTodos[position]||{}).todoText = newTodoText;
console.log(userTodos);
}
const userTodos=[{done:"object created"},{todoText:"nothing"}];
edit(3,"something else");
edit(2,"and also this");
edit(0,"fill it up!");
(userTodos[position]=userTodos[position]||{}) will create a new object at the given position if it does not yet exist, otherwise it will continue to work with (=extend) the already existing object.
I'm super newbie in coding and I need help to achieve this code.
I'm trying to get a random item (in pairs) from an array and then remove it from this array until user gets to the last item or 60 days have gone from using the service (cookie?)... I have build a script with the help of other questions here in stackoverflow and here is my results so far.
`<script>
var randomizer = document.getElementById("getImgBut");
var dog1 = '/app/wp-content/mediaApp/yo-creo-mi-realidad/01F.jpg';
var dog2 = '/app/wp-content/mediaApp/yo-creo-mi-realidad/01B.jpg';
var dogpics=[dog1,dog2];
var yourPics = [
dogpics,
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/02F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/02B.jpg' ],
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/03F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/03B.jpg' ],
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/04F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/04B.jpg' ],
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/05F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/05B.jpg' ],
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/06F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/06B.jpg' ] //This array has 52 cards but I cutted it for example purposes
];
function get_random_number(array){
return Math.floor(Math.random() * array.length |0);
} // here is where I have tried to modify with other scripts like the one in this page https://stackoverflow.com/questions/38882487/select-random-item-from-array-remove-it-restart-once-array-is-empty with no success
randomizer.addEventListener("click", function() {
var rand_number = get_random_number(yourPics);
console.log(rand_number);
document.getElementById('img1').src = yourPics[rand_number][0];
document.getElementById('img2').src = yourPics[rand_number][1];
});
var card = document.querySelector('.card');
card.addEventListener( 'click', function() {
card.classList.toggle('is-flipped');
});
</script>`
Thank you for your help!
I don't fully understand what you mean by "remove in pairs", but I'll answer presuming you mean you wish to remove the image ending in 02F.jpg at the same time as removing the image ending in 02B.jpg, and then 03F.jpg at the same time as 03B.jpg.
The solution to this that I will propose is that we will structure your data a bit differently to begin with. That is, if those images, the "B image" and "F image" are linked, we could keep them in the same `javascript object. This would look like:
var yourPics = [
{
bImage: '/app/wp-content/mediaApp/yo-creo-mi-realidad/02F.jpg',
fImage: '/app/wp-content/mediaApp/yo-creo-mi-realidad/02B.jpg'
},
{
bImage: '/app/wp-content/mediaApp/yo-creo-mi-realidad/03F.jpg',
fImage: '/app/wp-content/mediaApp/yo-creo-mi-realidad/03B.jpg'
}...]
This would then be an array of objects, rather than strings. We can access the bImage property of an object with just
myObject = yourPics[0]
myObject.bImage
We could delete one of those objects those at random via splice.
myRandomlyRemovedObject = yourPics.splice(myIndexToDeleteFrom, 1) would remove 1 object from yourPics at position of myIndexToDeleteFrom, which you presumably would choose randomly. myRandomlyRemovedObject would be assigned to the one object we removed.
I think this object based approach is safer since you will know for a fact that you will removed both matching strings at the same time.
Sorry, I'm not sure how else to word my question and, as such, am having a hard time finding any relevant solutions.
The Project
I'm creating a script that cleans batches of raw text from an Excel file...
...gathers each "cell" into an array, and pushes each "person" into a new array while attaching their respective bits of info to each person. The text itself contains things like first names, last names, and cities. Some people have multiple cities on new lines...
*All info is randomly generated and any real life comparisons are purely coincidental
The Problem
I'm able to clean the text up and dump the contents of each line to an array without any issues but the problem comes when I try to push "lines" that only have a city to the most recent "full person". For instance:
John Doe has lived in Anchorage, AK; New York, NY; and Des Moines, IA.
New York, NY and Des Moines, IA each end up on their own separate lines with each city being the only item in an array.
When I go to push a city into the new array I get:
Uncaught TypeError: Cannot read property 'city' of undefined
The Code
Here's the code I'm working with. Specifically, my issue is with the mergeSort(a) function:
function sanitize(rawData) { // Accessed from event handler on line 70
// Split the raw data into "lines"
const splitByLine = rawData.split("\n");
// Split each line into an array by "tabs"
const splitByTab = splitByLine.map(line => {
return line.split("\t");
});
// Trim each array cell and remove any empty cells
const cleaned = splitByTab.map(function(item) {
const scrubbed = item.map(chunk => {
return chunk.trim();
});
const trimmed = scrubbed.filter(chunk => {
return chunk !== "";
});
return trimmed;
});
// Remove any empty arrays
const droppedEmpties = cleaned.filter(arrayItem => {
return arrayItem.length > 0;
});
mergeSort(droppedEmpties); // Line 32
}
// Sort cities lived in for each person
function mergeSort(a) {
console.log(a);
function Person(firstName, lastName, gender, city, birthday) {
this.firstName = firstName;
this.lastName = lastName;
this.gender = gender;
this.city = [city];
this.birthday = birthday;
}
const people = [];
let toggleID = 0;
a.forEach(person => {
// If the entry is likely to be a person...
if (person.length > 1) {
// Set the ID for the last known person
toggleID = a.indexOf(person);
let newPerson = new Person(
person[0], // firstName
person[1], // lastName
person[2], // gender
person[3], // city
person[4] // birthday
);
people.push(newPerson);
console.log(people[toggleID]);
} else { // ...Else assume it's a city and push to the previous person.
people[toggleID].city.push(person[0]); // THROWS ERROR: Cannot read property of 'city' of undefined
}
});
}
// Get form submission
document.getElementById("my-form").addEventListener("submit", function(e) {
e.preventDefault();
const textarea = document.getElementById("my-textarea");
const rawData = textarea.value;
sanitize(rawData); // Line 1
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form id="my-form">
<button>Submit</button>
<textarea id="my-textarea" cols="150" rows="15">
Cynthia Hogsett Female Grand Rapids, MI 12/6/1983
Jacqueline Crosborne Female Syracuse, NY 3/19/1984
Ricky Johnson Male Tioga, ND 4/6/1972
Jimmy Jessup Male Ames, IA 10/27/1993
Williston, ND
Grand Forks, ND
Andrew Manchez Male El Paso, TX 3/1/2001
Sarah Smith Female Seattle, WA 7/19/1981
Esteban Faccin Male Gilbert, AZ 1/30/1973
Spokane, WA
Minot, ND
James Town, CO
</textarea>
</form>
<script src="main.js"></script>
</body>
</html>
My guess is that, although I'm defining let toggleID outside of the IF/ELSE scope, I can't mutate the value in the IF statement and access that updated value from within the ELSE statement.
Thanks in advance for any insight!
Well, the if block and the else block don't execute at the same time, so you need to hope that toggleID has been set in some previous iteration of the loop. That's hard to guarantee. But actually that's not your problem, toggleID is set as you expect it. The problem is rather that when one of the element is not a person that gets pushed to people, in the subsequent run the next person does get pushed to the next free index. Not to toggleID. You should see console.log(people[toggleID]); (inside the if block) logging undefined even before it throws the exception.
a.indexOf(person) (or better, the second argument of the forEach callback) will refer to the last index seen in the original a array, not the people array that's relevant for getting the previous person. Using it to index into people will occasionally come up with the wrong (no) result - as you don't create exactly one new person for each element in a, every time.
Instead, you can
not store a toggleID at all, but just dynamically look up the last element in the array using people[people.length-1] every time.
not store the index, but just the last newPerson object that you created.
get the correct index, e.g. by doing toggleID = people.length before the people.push(…), or by doing toggleID = people.push(…) - 1;
Alright so I stuck on the code reading docs.
Iam starting with JS so go easy one me =].
I've got and array calld Area
which contains few arguments
let Area = ["Kanig Village", "Fort Chune", "Shadowy Heights", ...];
I cannot change this array till specific part of my code is executed but after I wish to add to every position another value. How to I do that to get exacly like that :
let Area = ["Kanig Village 14:30", "Fort Chune 15:30", "Shadowy Heights 16:30", ...];
I THINK this is what you want to do:
https://jsfiddle.net/wxzrpjeL/
//Declare 2 arrays
let Area = ["Kanig Village", "Fort Chune", "Shadowy Heights"];
let b = ["1", "2", "3"];
//Execute a function for each element in the Area array
Area.forEach(function(val, i) {
//The function appends a space, and the corresponding element in the second array
Area[i] += " " + b[i];
});
// just to illustrate the result, throw the result to the screen as a string...
alert(Area.toString());
i hope this is your answer
let Area = ["Kanig Village", "Fort Chune", "Shadowy Heights"];
for(i=0;i<Area.length;i++)
{
var hour=14+i;
Area[i]+=" "+hour+" : 30";
}