P.S new to js,
I made an ios like calculator in js in which the basic functioning is the user enters the first number which gets stored in a variable , screen gets cleared then the user enters the second number which again needs to be stored in a variable. I am currently having problem in storing the second number.
const getnum = () => {
let firstnum = parseFloat(ans.innerText);
console.log("firstnum" + "=" + firstnum);
};
Copied this from internet to get the second number
operationperf = (operation) => {
if (!numinmem) {
numinmem = parseFloat(ans.textContent);
operatorinmem = operation;
ans.textContent = "";
return;
}
operatorinmem = operation;
ans.textContent = getResultOfOperationAsStr();
ans.textContent = "";
};
but this causes both the variables to have the same value so if i do 50+78 the calculator performs 50+50
working demo: https://megahedron69.github.io/ios-Calculator/
javascript file: https://github.com/Megahedron69/ios-Calculator/blob/main/app.js
First of all, it seems that your numinmem variable is not being used outside the scope of operationperf function for any further processing.
Also, in your following function:
const getResultOfOperationAsStr = () => {
const currentValueNum = parseFloat(ans.textContent);
const valueNumInMemory = parseFloat(ans.textContent);
let newValueNum;
if (operatorinmem === "addition") {
newValueNum = valueNumInMemory + currentValueNum;
} else if (operatorinmem === "subtraction") {
newValueNum = valueNumInMemory - currentValueNum;
} else if (operatorinmem === "multiplication") {
newValueNum = valueNumInMemory * currentValueNum;
} else if (operatorinmem === "division") {
newValueNum = valueNumInMemory / currentValueNum;
}
return newValueNum.toString();
};
It appears that your are using the current value of the element stored in ans variable for both sides of your operations (e.g. ansValueAsFloat + ansValueAsFloat), hence, producing the undesired behavior.
My recommendation would be to set any of the variables involved in the final operation to the value of numinmem and set the other to the current value in ans.
Hope I made myself clear.
Related
I want to be able to change the value of a global variable when it is being used by a function as a parameter.
My javascript:
function playAudio(audioFile, canPlay) {
if (canPlay < 2 && audioFile.paused) {
canPlay = canPlay + 1;
audioFile.play();
} else {
if (canPlay >= 2) {
alert("This audio has already been played twice.");
} else {
alert("Please wait for the audio to finish playing.");
};
};
};
const btnPitch01 = document.getElementById("btnPitch01");
const audioFilePitch01 = new Audio("../aud/Pitch01.wav");
var canPlayPitch01 = 0;
btnPitch01.addEventListener("click", function() {
playAudio(audioFilePitch01, canPlayPitch01);
});
My HTML:
<body>
<button id="btnPitch01">Play Pitch01</button>
<button id="btnPitch02">Play Pitch02</button>
<script src="js/js-master.js"></script>
</body>
My scenario:
I'm building a Musical Aptitude Test for personal use that won't be hosted online. There are going to be hundreds of buttons each corresponding to their own audio files. Each audio file may only be played twice and no more than that. Buttons may not be pressed while their corresponding audio files are already playing.
All of that was working completely fine, until I optimised the function to use parameters. I know this would be good to avoid copy-pasting the same function hundreds of times, but it has broken the solution I used to prevent the audio from being played more than once. The "canPlayPitch01" variable, when it is being used as a parameter, no longer gets incremented, and therefore makes the [if (canPlay < 2)] useless.
How would I go about solving this? Even if it is bad coding practise, I would prefer to keep using the method I'm currently using, because I think it is a very logical one.
I'm a beginner and know very little, so please forgive any mistakes or poor coding practises. I welcome corrections and tips.
Thank you very much!
It's not possible, since variables are passed by value, not by reference. You should return the new value, and the caller should assign it to the variable.
function playAudio(audioFile, canPlay) {
if (canPlay < 2 && audioFile.paused) {
canPlay = canPlay + 1;
audioFile.play();
} else {
if (canPlay >= 2) {
alert("This audio has already been played twice.");
} else {
alert("Please wait for the audio to finish playing.");
};
};
return canPlay;
};
const btnPitch01 = document.getElementById("btnPitch01");
const audioFilePitch01 = new Audio("../aud/Pitch01.wav");
var canPlayPitch01 = 0;
btnPitch01.addEventListener("click", function() {
canPlayPitch01 = playAudio(audioFilePitch01, canPlayPitch01);
});
A little improvement of the data will fix the stated problem and probably have quite a few side benefits elsewhere in the code.
Your data looks like this:
const btnPitch01 = document.getElementById("btnPitch01");
const audioFilePitch01 = new Audio("../aud/Pitch01.wav");
var canPlayPitch01 = 0;
// and, judging by the naming used, there's probably more like this:
const btnPitch02 = document.getElementById("btnPitch02");
const audioFilePitch02 = new Audio("../aud/Pitch02.wav");
var canPlayPitch02 = 0;
// and so on
Now consider that global data looking like this:
const model = {
btnPitch01: {
canPlay: 0,
el: document.getElementById("btnPitch01"),
audioFile: new Audio("../aud/Pitch01.wav")
},
btnPitch02: { /* and so on */ }
}
Your event listener(s) can say:
btnPitch01.addEventListener("click", function(event) {
// notice how (if this is all that's done here) we can shrink this even further later
playAudio(event);
});
And your playAudio function can have a side-effect on the data:
function playAudio(event) {
// here's how we get from the button to the model item
const item = model[event.target.id];
if (item.canPlay < 2 && item.audioFile.paused) {
item.canPlay++;
item.audioFile.play();
} else {
if (item.canPlay >= 2) {
alert("This audio has already been played twice.");
} else {
alert("Please wait for the audio to finish playing.");
};
};
};
Side note: the model can probably be built in code...
// you can automate this even more using String padStart() on 1,2,3...
const baseIds = [ '01', '02', ... ];
const model = Object.fromEntries(
baseIds.map(baseId => {
const id = `btnPitch${baseId}`;
const value = {
canPlay: 0,
el: document.getElementById(id),
audioFile: new Audio(`../aud/Pitch${baseId}.wav`)
}
return [id, value];
})
);
// you can build the event listeners in a loop, too
// (or in the loop above)
Object.values(model).forEach(value => {
value.el.addEventListener("click", playAudio)
})
below is an example of the function.
btnPitch01.addEventListener("click", function() {
if ( this.dataset.numberOfPlays >= this.dataset.allowedNumberOfPlays ) return;
playAudio(audioFilePitch01, canPlayPitch01);
this.dataset.numberOfPlays++;
});
you would want to select all of your buttons and assign this to them after your html is loaded.
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
const listOfButtons = document.getElementsByClassName('pitchButton');
listOfButtons.forEach( item => {
item.addEventListener("click", () => {
if ( this.dataset.numberOfPlays >= this.dataset.allowedNumberOfPlays ) return;
playAudio("audioFilePitch" + this.id);
this.dataset.numberOfPlays++;
});
I have an async function that on page load it runs a function that gets a JSON file and stores it in songResults. it then gets a random object from the JSON and takes some parameters from it and uses them to set the sources for some elements in my HTML file. I am having an issue with the callback for the event listener, the parameter I am passing through guessingGame() is undefined inside the guessingGame() function and I'm not sure why. any help would be muchly appreciated.
JS code
//A async function to run when the page loads to get the first song and album cover to display.
const onPageLoad = async () => {
let songResults = await getSongData();
let randomSong = songResults[Math.floor(Math.random() * songResults.length)];
audioSound.src = randomSong.song_path;
audioSound.load();
albumCover.src = randomSong.photo_path;
//An event listener for the submit button to run the guessing game function.
submitButton.addEventListener("click", guessingGame(randomSong.song_path))
};
//async function for when the button is clicked to check the answer in the input box to the json data.
const guessingGame = async (songPath) => {
//get the value of the input box
let input = document.getElementById("guessInputBox").value;
//check if the value of the input box matches the song path in the json data
if (input) {
if (input === songPath) {
alert('correct')
score++;
alert("that took " + score + " attempts");
score = 0;
changeSong();
} else {
alert('incorrect')
alert(songPath);
score++;
};
};
};
What a json file response looks like
{
"song_name": "Listen to Us",
"release_date": "1/05/2012",
"album": "Home Brew",
"photo_path": "/pictures/home-brew.jpg",
"song_path": "/songs/homebrewski-listenToUs.mp3"
}
so the new code updates it kind of it works the first time you guess but the second time you guess it says your incorrect then correct and keeps looping heaps really weird and lags out the browser.
const onPageLoad = async () => {
let songResults = await getSongData();
let randomSong = songResults[Math.floor(Math.random() * songResults.length)];
audioSound.src = randomSong.song_path;
audioSound.load();
albumCover.src = randomSong.photo_path;
//An event listener for the submit button to run the guessing game function.
submitButton.addEventListener("click", () => {
guessingGame(randomSong.song_name);
});
};
//async function for when the button is clicked to check the answer in the input box to the json data.
const guessingGame = async (songPath) => {
//get the value of the input box
let input = document.getElementById("guessInputBox").value;
//check if the value of the input box matches the song path in the json data
if (input) {
if (input.toLowerCase() === songPath.toLowerCase()) {
alert('correct')
score++;
alert("that took " + score + " attempts");
score = 0;
changeSong();
} else {
alert('incorrect')
alert(songPath);
score++;
};
};
};
//need to change this to an async function once figured out how to store data.
const changeSong = async () => {
let songResults = await getSongData();
let randomSong = songResults[Math.floor(Math.random() * songResults.length)];
audioSound.src = randomSong.song_path;
audioSound.load();
albumCover.src = randomSong.photo_path;
submitButton.addEventListener("click", () => {
guessingGame(randomSong.song_name);
});
};
Updated Answer
Thank you for posting more code. It looks like you may have mistakenly passed along the song_name instead of the song_path:
// From the onPageLoad and changeSong functions
guessingGame(randomSong.song_name);
// From the guessingGame function
input.toLowerCase() === songPath.toLowerCase()
Another thing to consider is your click-handler; you're adding one to the submitButton every time a song is loaded. As such, the first time you click the button, a single event handler is called. The second time you click, two are called. The third time, three, etc.
Your submit button behavior really only needs to be set once, and forgotten. It's job is to do one thing: see if the user's selection matches the currently-playing song. And, if the user got the correct answer, load another song:
// Minimal game state
let score = 0;
let songs = [];
let songIndex = -1;
// References to game elements
const submit = document.querySelector("#submit")
const selection = document.querySelector("options");
// Begin game functionality
async function onPageLoad () {
// Load meta data for all songs into `songs`
songs = await getSongData();
// TODO: Populate #options based on `songs` data
setRandomSong();
}
function setRandomSong () {
songIndex = Math.floor( Math.random() * songs.length );
audioSound.src = songs[ songIndex ].path;
albumCover.src = songs[ songIndex ].photo;
}
// This will be called whenever a song is changed
audioSound.addEventListener( "load", function audioLoaded () {
console.log( "Audio has loaded, game can be played." );
});
// We need only a single handler for our guess-button
submit.addEventListener( "click", function guess () {
// Get our values to compare
const guess = selection.value;
const answer = songs[ songIndex ].name;
// Increment the number of attempts
score += 1;
// Check lowercase values for equality
// If the answer is false, share number of attempts
if ( guess.toLowerCase() !== answer.toLowerCase() ) {
alert( `You have guessed ${ score } times.` );
return;
}
// If the answer was correct, reset game state
alert( `Correct! And after ${ score } guesses.` );
score = 0;
setRandomSong();
});
Note, this code is untested, but should clearly define a suitable approach to your problem. Please feel free to engage further within the comments as needed.
Original Answer
The addEventListener method expects a function as the second argument. Look closely at your code:
submitButton.addEventListener("click", guessingGame(randomSong.song_path))
Note that you're executing guessingGame, rather than referencing it. Instead, you'd want to provide another function:
submitButton.addEventListener("click", function () {
guessingGame( randomSong.song_path );
});
Now, when the submitButton is clicked, our anonymous function will be called, which in turn will pass randomSong.song_path to guessingGame.
I have 2 soy.js and lib-dialogs.js files.
I need to make lib-dialogs pass the value of the lineCount variable to soy.js.
I was able to do this with localStorage but because it saves in a cookie it does not update the values correctly.
In lib-dialogs there is a function called BlocklyDialogs.congratulations that calls the necessary data.
FIle:lib-dialogs.js
BlocklyDialogs.congratulations = function() {
// Add the user's code.
if (BlocklyGames.workspace) {
var linesText = document.getElementById('dialogLinesText');
linesText.textContent = '';
// Line produces warning when compiling Puzzle since there is no JavaScript
// generator. But this function is never called in Puzzle, so no matter.
var code = Blockly.JavaScript.workspaceToCode(BlocklyGames.workspace);
code = BlocklyInterface.stripCode(code);
var noComments = code.replace(/\/\/[^\n]*/g, ''); // Inline comments.
noComments = noComments.replace(/\/\*.*\*\//g, ''); /* Block comments. */
noComments = noComments.replace(/[ \t]+\n/g, '\n'); // Trailing spaces.
noComments = noComments.replace(/\n+/g, '\n'); // Blank lines.
noComments = noComments.trim();
var lineCount = noComments.split('\n').length;
var pre = document.getElementById('containerCode');
pre.textContent = code;
if (typeof prettyPrintOne == 'function') {
code = pre.innerHTML;
code = prettyPrintOne(code, 'js');
pre.innerHTML = code;
}
if (lineCount == 1) {
var text = BlocklyGames.getMsg('Games_linesOfCode1');
} else {
var text = BlocklyGames.getMsg('Games_linesOfCode2')
.replace('%1', String(lineCount));
}
linesText.appendChild(document.createTextNode(text));
}
FIle:soy.js
example "var count = BlocklyDialogs.congratulations(lineCount);"
In soy.js I need to receive the values of lineCount. I've already managed to do this using localStorage but I needed to do something more direct.
In testing I verified that the problem is in the lineCount variable because it is not passing a value to any variable even within the file itself.
I created a variable outside the blocklyDialogs.congratulations function and entered a value of 5.
I called the variable in the soy.js file and got it normally.
I need to make the lineCount pass its value.
You can use event driven programming, pub-sub model.
class EventManager {
constructor(...listeners) {
this.listeners = listeners;
}
register(fn) {
const id = this.listeners.push(fn);
return () => {
this.listeners.splice(id - 1, 1);
};
}
emit(data) {
this.listeners.forEach(fn => fn(data));
}
}
const pushEvents = new EventManager();
// JS 1
const unsubscribe1 = pushEvents.register(x => {
console.log("event:", x);
});
pushEvents.register(x => {
console.log("event:", x);
});
// JS 2
pushEvents.emit("Tets data");
//Output
// event: Tets data
// event: Tets data
unsubscribe1();
pushEvents.emit("Tets data2");
//Output
// event: Tets data2
.as-console-row {color: red!important}
what i want to achieve, is try to increase the precison of the values returned by the heart beat sensor of a Tizen smartwatch.
The values are Float64 numbers, since the language is Javascript.
I tried to use a function like this:
function strip(interval) {
return (parseFloat(interval).toPrecision(4));
}
but with no success. Maybe i'm doing something wrong, like doing some programming mistakes, i really don't know. Apparently, the IDE compile and build the package to install with no problem, but i can't see something different with or without this function included.
I will post my entire code below. Please check when is created the function strip . I've used the escamotage if (interval !== 0) {
interval_screen = interval;
} because i don't want the zeros to be printed. Please note that i want the variable streamed to the ROS topic HeartRateInterval to remain a Float; this is why i've also used the parseFloat function.
Thank you!
Code :
document.addEventListener('tizenhwkey', function(e) {
if(e.keyName === "back")
window.webapis.motion.stop("HRM");
tizen.application.getCurrentApplication().exit();
});
function Connect(){
var ip;
var connection=false;
var interval_screen = 0;
if (document.getElementById("ip").value==="")
{
ip="10.42.0.1";
}
else
{
ip=document.getElementById("ip").value;
}
var ros = new ROSLIB.Ros({
url : 'ws://' + ip +':9090'
});
ros.on('connection', function() {
connection=true;
document.getElementById("Connection_status").setAttribute("color","green");
document.getElementById("Connection_status").innerHTML = 'Connected';
tizen.power.request("SCREEN", "SCREEN_DIM");
});
ros.on('error', function(error) {
document.getElementById("Connection_status").setAttribute("color","orange");
document.getElementById("Connection_status").innerHTML = 'Error';
});
ros.on('close', function() {
document.getElementById("Connection_status").setAttribute("color","red");
document.getElementById("Connection_status").innerHTML = 'Unconnected';
connection=false;
tizen.power.release("SCREEN");
});
var RatePub = new ROSLIB.Topic({
ros : ros,
name : '/HeartRateData',
messageType : 'std_msgs/Float64'
});
var IntervalPub = new ROSLIB.Topic({
ros : ros,
name : '/HeartRateInterval',
messageType : 'std_msgs/Float64'
});
window.webapis.motion.start("HRM", onchangedCB);
function onchangedCB(hrmInfo)
{
var rate = hrmInfo.heartRate;
document.getElementById("mytext").innerHTML = 'Heart Rate= ' + rate + ' bpm';
var interval = hrmInfo.rRInterval/1000;
function strip(interval) {
return (parseFloat(interval).toPrecision(4));
}
if (interval !== 0) {
interval_screen = interval;
}
document.getElementById("mytext1").innerHTML = 'RR Interval= ' + interval_screen + ' s';
var Float64 = new ROSLIB.Message({
data:rate
});
if(connection===true)
{
RatePub.publish(Float64);
}
else
{
document.getElementById("mytext").innerHTML = 'Heart Rate = 0 bpm';
}
var Float64 = new ROSLIB.Message({
data:interval
});
if(connection===true)
{ if (interval !== 0) {
IntervalPub.publish(Float64);
}
else {
}
}
else
{
document.getElementById("mytext1").innerHTML = 'RR Interval = 0 s';
}
}}
Am I missing something here, but I can not find where you actually call that new function?
And why do you create it inline inside the onchangedCB function?
It looks as if you expected that function to be called because you declare it there and call the parameter the same as the interval variable. Which will not work (as far as I know in any programming language).
Then what I would try is call that function parseFloat(interval).toPrecision
directly instead of putting it in another function.
But what I'm far more interested in is:
here hrmInfo.rRInterval/1000
the orginal value is devived by a thousand.
Remove that division (like this var interval = hrmInfo.rRInterval;) and see if there actually are more numbers where the decimal point would be.
I can not make it up from your example, but if the value normally is something like 120 per minute. And you want to know if there are more precise values behind that, then the value should now look something like 1200054 if it is all zeroes like 120000 all the time, then the systems creating that event does not give off a more precise measure.
I converted this JavaScript to Java and it seems to work great, but some glitches still remain I'm trying to fix them and so far this is one of the problems I think exist.
JavaScript anonymous function remapped on same array twice do both mappings work or old one is replaced? if it's replaced why does this exist here? I'm 99% certain it gets replaced with the next functions, since it could be either cGBC or not cGBC there should be no reason this line this.memoryHighWriter[0x2] = this.memoryHighWriteNormal; should be in any effect, but I'm not sure.
As far as I understand that memoryWriteJumpCompile function also initializes all addresses from 0 to 0xFFFF with whatever each address does if it has no use later in the future it still gets a default operation..
But why even give a default operation for this.memoryHighWriter[0x2] = this.memoryHighWriteNormal; if it still gets replaced either way in the next function.
The full source code is at : https://raw.githubusercontent.com/grantgalitz/GameBoy-Online/master/js/GameBoyCore.js
This below call is called in memoryWriteJumpCompile function.
this.registerWriteJumpCompile(); //Compile the I/O write functions separately...
GameBoyCore.prototype.registerWriteJumpCompile = function () {
//I/O Registers (GB + GBC):
...
//SC (Serial Transfer Control):
this.memoryHighWriter[0x2] = this.memoryHighWriteNormal;
this.memoryWriter[0xFF02] = this.memoryWriteNormal;
...
this.recompileModelSpecificIOWriteHandling();
this.recompileBootIOWriteHandling();
};
The recompileModelSpecificIOWriteHandling(); is called right after the registerWriteJumpCompile is finished.
GameBoyCore.prototype.recompileModelSpecificIOWriteHandling = function () {
if (this.cGBC) {
//GameBoy Color Specific I/O:
//SC (Serial Transfer Control Register)
this.memoryHighWriter[0x2] = this.memoryWriter[0xFF02] = function (parentObj, address, data) {
if (((data & 0x1) == 0x1)) {
//Internal clock:
parentObj.memory[0xFF02] = (data & 0x7F);
parentObj.serialTimer = ((data & 0x2) == 0) ? 4096 : 128; //Set the Serial IRQ counter.
parentObj.serialShiftTimer = parentObj.serialShiftTimerAllocated = ((data & 0x2) == 0) ? 512 : 16; //Set the transfer data shift counter.
} else {
//External clock:
parentObj.memory[0xFF02] = data;
parentObj.serialShiftTimer = parentObj.serialShiftTimerAllocated = parentObj.serialTimer = 0; //Zero the timers, since we're emulating as if nothing is connected.
}
}
...
} else {
...
//Fill in the GameBoy Color I/O registers as normal RAM for GameBoy compatibility:
//SC (Serial Transfer Control Register)
this.memoryHighWriter[0x2] = this.memoryWriter[0xFF02] = function (parentObj, address, data) {
if (((data & 0x1) == 0x1)) {
//Internal clock:
parentObj.memory[0xFF02] = (data & 0x7F);
parentObj.serialTimer = 4096; //Set the Serial IRQ counter.
parentObj.serialShiftTimer = parentObj.serialShiftTimerAllocated = 512; //Set the transfer data shift counter.
} else {
//External clock:
parentObj.memory[0xFF02] = data;
parentObj.serialShiftTimer = parentObj.serialShiftTimerAllocated = parentObj.serialTimer = 0; //Zero the timers, since we're emulating as if nothing is connected.
}
}
}