How to loop through functions by name in P5js / JavaScript - javascript

I'm working on a basic P5 program that requires up to ten input boxes.
So I need to first create the button instances, using e.g.
factor1Input = createInput(""); // create DOM element for input field
factor1Input.position(leftMargin, topMargin + 50); // place button on screen
factor1Input.changed(this.factor1update); // call function when value changes
factor1Button = createButton('Update Factor'); // create DOM element for button
factor1Button.position(100, 100); // position button
Then toggle their visibility using e.g.
factor1Input.show(); // toggle display on
factor1Button.show();
factor1Input.hide(); // or toggle it off
factor1Button.hide();
But because I'll have up to 10, this will require a ton of repetitive code.
So I want to create a loop that goes something like (e.g. just for the show function);
for (let i = 1; i < factorCount; i++){
let fci = "factor" + i + "Input";
let fcb = "factor" + i + "Button";
fci.show();
fcb.show();
}
But I'm getting the following error:
Uncaught TypeError: fci.show is not a function
Which suggests some kind of type mismatch, i.e. I can't seem to just compile a string, and have this recognized as the JavaScript function.
Any suggestions?

fci will be a string so the String class will not have a method show, You will get an exception. Instead
You can write in this way
var factorObject = {
factor1Input:createInput("")
}
factorObject['factor1Input'].position(leftMargin, topMargin + 50);
For show
factorObject['factor1Input'].show();
Here in the loop
for (let i = 1; i < factorCount; i++){
let fci = "factor" + i + "Input";
factorObject[fci].show();
}

The reason why your code doesn't work is because you create a string and try to call it like a variable with the same name
let fci = "factor" + i + "Input";
fci.show(); // fci is just a string 'factor1Input', has nothing in common with factor1Input variable
You should use arrays Arrays and instead of keeping input number (e.g. index) inside a name, let it be the index of an item in the array
const factorCount = 10
const inputs = []
const buttons = []
// example of creating inputs and buttons in a loop, you can create them manually if you want,
// but don't forget to .push them to respective array
for (let i = 1; i < factorCount; i++){
const input = createInput("");
input.position(leftMargin, topMargin + 50 * i); // using index to calculate top margin
input.changed((value) => this.factorUpdate(i, value)); // notice the change here
inputs.push(input)
const button = createButton('Update Factor');
button.position(100, 100 + 50 * i); // also using index to calculate top margin
buttons.push(button)
}
function showInput(index) {
inputs[index].show()
buttons[index].show()
}
function hideInput(index) {
inputs[index].hide()
buttons[index].hide()
}
showInput(3) // shows 3rd input and button
hideInput(4) // hides 4th input and button
Notice also how I changed your this.factor1update method call. The same way you don't want to have 10 separate variables for 10 elements, you don't want to have 10 methods to handle changes on those 10 elements (what if there was 10000 elements?). Instead, create one method factorUpdate that will receive item index and the value that was changed and use that to handle the input change
added:
for (let i = 0; i < factorCount; i++){ // changed 1 to 0 here, it was a typo
const input = createInput("");
input.position(leftMargin, topMargin + 50 * i);
input.changed(() => factorUpdate(i)); // we call factorUpdate with index of an element
inputs.push(input)
const button = createButton('Update Factor');
button.position(185, topMargin + 50 * i);
buttons.push(button)
}
function factorUpdate(i, event){
// argument i is now an index of unfocused input
console.log("input index: " + i + ", value: " + inputs[i].value());
}
Note also how input.changed() works: you edit the input, then you click somewhere else on the page to unfocus it, and that's when this event is triggered. With that in mind, buttons here don't actually do anything as there are no click listeners assigned to them

Related

How to Update a Variable that Relies on Code After It? (JavaScript)

My Problem:
Hey everyone. I'm trying to write some code that changes the brightness of an element based on how far away it is from the current item. Everything runs according to plan, however, as it moves down the list all of the items before the current item begin to return "NaN" as their value. This makes sense, as the "currentItemIndex" variable is only given a value once it finds the current item's position, so there's no value for the "currentItemIndex" until it reaches the current item within the list.
What I've Tried:
I've tried declaring the "currentItemIndex" variable before it searches for the current item, giving it either a value of 0 to start or an empty string. The starting value of 0 results in the "currentItemIndex" staying at 0 no matter what, and the empty string just produces the same result as when there was no variable declared there in the first place.
I'm not sure how to get the "currentItemIndex" before searching for the current item and not have it affect the variable when it checks for the current item. Is anyone able to help me out?
My Code:
JavaScript:
var items = document.getElementsByClassName('item'); // Gets all of the items
for (i = 0; i < items.length; i++) { // For each of the items, this:
var itemClass = items[i].classList // Get class of the item
if (itemClass.contains('current-item')) { // If it is the current item, this:
var currentItemIndex = i; // Set the current item's position to the target's position
}
var brightness = (100 + (Math.abs(currentItemIndex - i) * 50)); // Calculate how much the brightness should change based on the target's distance from the current item
items[i].style.filter = 'brightness(' + brightness + '%)'; // Apply that brightness to the target
}
You'll need to find the currentItemIndex first, then do the loop setting the brightness:
const items = document.getElementsByClassName("item"); // Gets all of the items
let currentItemIndex;
for (let i = 0; i < items.length; i++) { // For each of the items, this:
const itemClass = items[i].classList // Get class of the item
if (itemClass.contains("current-item")) { // If it is the current item, this:
currentItemIndex = i; // Set the current item"s position to the target"s position
break;
}
}
for (let i = 0; i < items.length; i++) { // For each of the items, this:
const brightness = (100 + (Math.abs(currentItemIndex - i) * 50)); // Calculate how much the brightness should change based on the target"s distance from the current item
items[i].style.filter = "brightness(" + brightness + "%)"; // Apply that brightness to the target
}
(With more context, it may be that there's a more concise way to find the index of that item [the first for loop], but the above works and it's simple.)
Side note: I added a declaration for i, so the code isn't relying on what I call The Horror of Implicit Globals. I strongly recommend using strict mode, so that's the error it always should have been.
You need to break the problem down into two steps:
find the index of the current item
calculate and set the brightness for every item
// get all of the items as an array
let items = Array.from(document.getElementsByClassName('item'));
// 1. find the index of the current item
let currentItemIndex = items.findIndex(item => items.classList.contains('current-item'));
// 2. calculate and set the brightness for every item
items.forEach((item, i) => {
let brightness = (100 + (Math.abs(currentItemIndex - i) * 50));
item.style.filter = `brightness(${brightness}%)`;
});

how do i create a simple onclick add function()

i try to do these code and expecting it for increase by 1 everytime i click on the button but it returns me NaN instead.
im really new to javascript. really hope someone could help me!
thanks in advance.
function add(){
var sum = parseInt(1);
var adding = adding + sum;
document.getElementById("amt1").innerText = adding;
}
I see, here you've asked two problems:
Why adding is NaN
At line #2, you haven't initialized variable adding, hence in RHS adding is undefined.
Therefore, the RHS block adding + sum; is evaluated as undefined + 1, which evaluates to NaN
How to use onClick()
W3School's tutorial on onClick()
Here is your code in working state (HTML + JavaScript):
var adding = 0; // initialization. This is the step, that your code was missing
function add() {
var sum = parseInt(1);
adding = adding + sum;
document.getElementById("amt1").innerText = adding;
}
<h1>The onclick Event</h1>
<button onclick="add()">Click me</button>
<p id="amt1"></p>
You could take a closure over the sum and take the returned function for adding to the value.
var add = function(sum) {
return function () {
document.getElementById("amt1").innerHTML = ++sum;
};
}(0);
<span id="amt1">0</span> <button onclick="add()">add</button>
You're making the assignment in the local scope of the function, so every time the function executes, it's going to assign the value 1 to the 'sum' variable. Next, you're creating the variable 'adding' by trying to assign the value of adding, which doesn't exist yet.
It seems like the goal of your function is to just increment the value of 'amt1' by one.
function add(elId){
let currentAmt = document.getElementById(elId).innerText;
currentAmt = parseInt(currentAmt) + 1;
document.getElementById(elId).innerText = currentAmt;
}
By passing in the element ID, your function can now be applied to any element. It parses the integer from its current inner text, adds 1, then sets the new amount to the inner text of the element.
you might need to have a look on this post- Increment value each time when you run function
about how to increment, the idea is keep the variable outside the function as in you case,
you dont need parseInt as its used for parsing integers from a string.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt
you need to keep the sum variable outside function. following is the general function to add
var n = 0;
function add(value){
n += value;
return n;
}
try it
document.getElementById("amt1").addEventListener("click", displayDate);
function displayDate() {
var node = document.getElementById('amt1');
document.getElementById("amt1").innerHTML = parseInt(node.textContent)+1;
}
<button id="amt1">1</button>
You're using an undefined variable adding to make your calculation that's why you get a NaN as a result :
var adding = adding + sum : the variable adding isn't yet initialized so it's value equals to undefined which give us var adding = undefined + sum = NaN. See next example :
var sum = parseInt(1);
console.log('typeof "adding" before initialization is ' + typeof adding + ' and it equals "' + adding + '"');
var adding = adding + sum;
console.log('typeof "adding" after initialization is ' + typeof adding + ' and it equals "' + adding + '"');
BTW, you don't need parseInt in order to put manually a number, sum = parseInt(1) is the same as sum = 1 but the later is faster.
And now here's how to accomplish your task :
/**
* btn: the button to be clicked to increment the counting.
* counterTxt: the "p#counter" element that prints the counting.
* counter: keeps track of the number of the clicks made.
**/
const btn = document.getElementById('btn'),
counterTxt = document.getElementById('counter');
let counter = 0; /** holds the number of clicks **/
/** click event handler for the "button" **/
btn.addEventListener('click', () => counterTxt.textContent = ++counter); /** update the "p#counter" text and increment the counter **/
<button id="btn">click to increment</button>
<p id="counter">0</p>

Creating and overwriting variables dynamic per user input

In the following code, the user is able to create variables utilizing the window object via an input type text element. I've written a function that console logs the name of the variable followed by the value of 0 in which the variable is initialized. This only occurs when the following key string literal, "-nr " precedes the desired name for the created variable.
The goal of this exercise is to increment any created variable value by 1 when the variable name is reentered into the input element. My attempt at doing so is by first writing the first function, varCreate to declare and initialize variables to 0, push them into an array, and console log the variable name followed by its value. The next function which I have a problem with (varPlus) is meant to add 1 to the value of each value when a particular name is entered into the input element however, it adds a few more than 1 even when I utilize a for loop to evaluate if the string literal value of the input element value property is equivalent to each element of the array varArray.
const _in = document.getElementById('in');
var varArray = [];
function varCreate(e) {
let _key = e.key;
if(_key === "Enter") {
if(_in.value.substring(0, 4) == "-nr ") {
window[_in.value.substring(4).replace(/\s/g, "_")] = 0;
varArray.push(_in.value.substring(4).replace(/\s/g, "_"));
console.log("var: " + varArray[varArray.length - 1] + "\nvalue: " + window[varArray[varArray.length - 1]]);
_in.value = "";
}
}
}
function varPlus(e1) {
let _key1 = e1.key;
if(_key1 === "Enter") {
checker = new RegExp(_in.value.replace(/\s/g, "_"), "gi");
for(var i = 0; i < varArray.length; i++) {
if(checker.test(varArray[i])) {
window[varArray[i]] += 1;
console.log("var: " + varArray[i] + "\nvalue: " + window[varArray[i]]);
}
}
delete window["checker"];
}
}
_in.addEventListener('keydown', varCreate);
_in.addEventListener('keydown', varPlus);
<input id='in' type='text' />
The end result when attempting to utilize varPlus is that it'll console log all variable names and values which somehow increment in value when it should only be console logging only the variable name which I'm trying to access via user input followed by its value. I would greatly appreciate it if anyone can shed some light on how I'm encountering these errors.
First of all it is really helpful if you try and make your code executable :)
Now for the user generated variables you could do something like this:
// DOM Elements
const input_variable = document.getElementById("input_variable");
const button_createVariable = document.getElementById("button_createVariable");
// Variables
let userVariables = {};
// Event listeners
window.addEventListener("keyup", event => {if(event.key == "Enter") parseVariable()});
button_createVariable.addEventListener("click", parseVariable);
function parseVariable() {
// Get the variable name and remove all spaces
let variableName = input_variable.value.substring(0, input_variable.value.indexOf("=")).replace(/\s+/g, '');
// Get the variable value and remove all spaces
let variableValue = input_variable.value.substring(input_variable.value.indexOf("=") + 1, input_variable.value.length).replace(/\s+/g, '');
// Add the variable to the object
userVariables[variableName] = variableValue;
// Clear the input
input_variable.value = "";
// Log the object into the console
console.log(userVariables);
}
<input id='input_variable' type='text'/><button id="button_createVariable">Create</button>
WARNING You of course still need to verify the user input. At this state it will accept everything as input. But now you can loop through the object and count up (or whatever) if already exists.
Oh yes btw, the syntax is simply: <name> = <value> eg. foo = 10.. unimportant detail :P

Javascript <li> onclick merged

I am attempting to load Case Studies through a clickable list of dynamically generated items. However my issue is, while it loads them properly, and if you click the elements, it loads a Case Study correctly, but it always loads the LAST case study assigned.
For example, if I have Case Study 1, Case Study 2 and Case Study 3, clicking any of the options takes me to Case Study 3.
function MakeList(array){
//Create List Element
var list = document.createElement("ul");
//For each of the Array Elements
for(var i = 0; i < array.length; i++)
{
//Create a ListItem Element
window["listItem" + i] = document.createElement("li");
eval("listItem" + i).setAttribute("id", ("listItem" + i));
//And if it exists
if(array[i] != undefined)
{
//Use the array elements title variable for the text node and localize the remaining object variables
eval("listItem" + i).appendChild(document.createTextNode(array[i].title));
var title = array[i].title;
var icon = array[i].icon;
var imageOne = array[i].imageOne;
var imageTwo = array[i].imageTwo;
var challenge = array[i].challenge;
var solution = array[i].solution;
var results = array[i].results;
//Give the ListItem some Styling
eval("listItem" + i).style.cursor = "pointer";
eval("listItem" + i).style.color = "blue";
eval("listItem" + i).style.margin = "0 0 1vh 0";
//And add the onclick function to Load the selected CaseStudy
eval("listItem" + i).onclick = function()
{
RemoveContent();
LoadCaseStudy(title, icon, imageOne, imageTwo, challenge, solution, results);
};
//Add to the List
list.appendChild(eval("listItem" + i));
}
}
//Return the List
return list;}
I have tried giving them dynamically assigned IDs and variable names to seperate the on-click call, but no success. Any advice?
At that point in time when you eval the code in the onclick the FOR loop has already finished it's execution and "I" is at the .length - 1 of your array.
You should do something like this:
1. First declare your onclick handler outside of the code of the FOR loop
function handler(title, icon, imageOne, imageTwo, challenge, solution, results)
{
RemoveContent();
LoadCaseStudy(title, icon, imageOne, imageTwo, challenge, solution, results);
}
Attach the handler in a bit different way:
eval("listItem" + i).onclick = handler.bind(null, title, icon, imageOne, imageTwo, challenge, solution, results);
Where "null" can be an object representing your desired execution context.
On another note
Avoid using "EVAL" at all cost. If you explain your case a bit better I will help you write the code without it. Give some HTML examples, or explain how the HTML is being built.

jquery append performed link replace

inputTabTitle: function(){
origin = template.clone();
$("#inputTabCount").change(function(){
tabcount = parseInt($("#inputTabCount").val());
if(tabcount > 0){
tab = origin.find("label").text();
for(i = 1; i <= tabcount; i ++){
origin.find("label").text(tab + i);
origin.find("label").attr("for", "inputTabTitle" + i);
origin.find("input").attr("id", "inputTabTitle" + i);
$("#tabCount").append(origin);
}
}
})
}
set n = 3
When append to "#tabCount", only one element insert, actually should be three.But this code append performed like replace.Why?
And when I add "origin = origin.clone()" before loop end, it worked well, three element inserted.
You clone your template only once. That means: Two times you append the 'origin' to a place, where it already is.
To get, what you want (or I think you want), the cloning MUST be in the loop.
Please notice further that you pollute the GLOBAL space when you define variables such as 'tabcount' without the 'var'. I fixed that in your source code, too.
Rewrite the function like that below.
But be warned: The amount of tabs is being inserted every time the value changes. That means:
Value changes to 1 --> one tab is made
Value changes to 2 --> two ADDITIONAL tabs are made.
.
inputTabTitle: function(){
$("#inputTabCount").change(function(){
var tabcount = parseInt($("#inputTabCount").val());
if(tabcount > 0){
tab = template.find("label").text();
for(i = 1; i <= tabcount; i ++){
var origin = template.clone();
origin.find("label").text(tab + i);
origin.find("label").attr("for", "inputTabTitle" + i);
origin.find("input").attr("id", "inputTabTitle" + i);
$("#tabCount").append(origin);
}
}
})
}

Categories

Resources