Check whether function has run fully before, based on variable - javascript

I have a function which "types" out a header title as though it is being typed on the screen.
The typer only starts typing once a particular section of my site is "active" or is seen on the screen.
At present, it takes the outputID aka the area where this text will be typed into. There are two instances of this function being run, each with different outputIDs - I only want the function to run once per outputID.
This is how the function is initially called.
<h2 id="typer-get-in-touch" class="typer" data-text="Get in Toche^^^^^ Touch"></h2>
if(anchorLink == 'contact'){
var outputID = $("#typer-get-in-touch");
textTyping(outputID);
}else if(anchorLink == 'expertise'){
var outputID = $("#typer-expertise");
textTyping(outputID);
}
This is the textTyping function
function textTyping(outputID){
$(outputID).show();
var textString = $(outputID).data("text");
var textArray = textString.split("");
var texttypeing = setInterval(
function() {
typeOutText(outputID,textArray);
}, 170);
function typeOutText(outputID,textArray) {
if (textArray[0] == "^"){
outputID.text(function(index, text){
return text.replace(/(\s+)?.$/, '');
});
textArray.shift();
}else {
if (textArray.length > 0) {
outputID.append(textArray.shift());
} else {
clearTimeout(texttypeing);
}
}
}
}
My issue at present is that the function runs multiple types, and continues to type each time the original anchorLink trigger is achieved. The result is that is writes the title many times e.g:
Get In TouchGet In TouchGet In Touch
Each time the section is navigated to, the typing starts again.
How can I run this function only ONCE per outputID? So once the outputID has been used, the function can no longer run for that data?
JSFiddle of non-working example: https://jsfiddle.net/qLez8zeq/
JSFiddle of mplungjan's solution: https://jsfiddle.net/qLez8zeq/1/

Change
function textTyping(outputID){
$(outputID).show();
var textString = $(outputID).data("text");
to
function textTyping(outputID){
var textString = $(outputID).data("text");
if (textString=="") return;
$(outputID).data("text","");
$(outputID).show();
FIDDLE

What you need to do is to bind the event handler for each ID and then unbind it after it's been triggered the first time. Since you're already using jQuery, you can use the "one" method to do exactly this for each outputID:
$( "#typer-get-in-touch" ).one( "click", function() {
textTyping(outputID);
});

I suppose you could store your processed outputIds into an array and then check if the given outputId is present in the array before starting?
Define your array, check for the existence, if not found, do code example:
var processedIds = [];
function textTyping(outputID) {
var foundItem = false;
for (var i = 0; i < processedIds.length; i++)
{
if (processedIds[i] == outputID) {
foundItem = true;
break;
}
}
if (!foundItem) {
//the rest of your code goes here
}
}

You can add some check at the beginning of your function:
var called = {};
function textTyping(outputID) {
if (called[outputID]) {
return;
}
called[outputID] = true;
// your code
}

Related

Reset function that will reset fields to what they were based on which of two functions was last run

I'm making a simple browser game to practice JS where you kill a bad guy. There are 2 modes, easy and hard. I am trying to set up a reset button that will reset information fields depending on which of the modes the game is in.
example
playing easy, click reset and the game resets by running easyMode function
playing hard, click reset and the game resets by running hardMode function
apologies if this is simple, this is why I'm making practice games
I've tried to make the function that currently has a specific class selected run when running reset function
var resetButton = document.querySelector("#new");
var easy = document.querySelector("#easy");
var hard = document.querySelector("#hard");
var playerHealth = document.querySelector("#php");
var playerFocus = document.querySelector("#pfocus");
var bossHealth = document.querySelector("#bhp");
var attack = document.querySelector("#attack");
var strong = document.querySelector("#strong");
var regenerate = document.querySelector("#regen");
var modeButtons = document.querySelectorAll(".mode");
var defenseLog = document.querySelector("#defenselog");
var offenseLog = document.querySelector("#offensivelog")
var boss = {}
var player = {}
setupModeButtons();
easyMode();
reset();
function hardMode(){
player.health = 12;
player.focus = 15;
boss.health = 25;
update()
};
function easyMode(){
player.health = 10;
player.focus = 10;
boss.health = 12;
update();
}
function update (){
playerFocus.textContent = player.focus;
playerHealth.textContent = player.health;
bossHealth.textContent = boss.health;
};
function setupModeButtons(){
for(var i = 0; i < modeButtons.length; i++) {
modeButtons[i].addEventListener("click", function(){
modeButtons[0].classList.remove("selected");
modeButtons[1].classList.remove("selected");
this.classList.add("selected");
});
}
}
function reset (){
if(easyMode.classList=="selected"){
easyMode();
} else if(hardMode.classList=="selected") {
hardMode();
}
}
The reset button works but always resets with function easyMode no matter which has had the class "selected" applied with function setupModeButtons
element.classList is a DomTokenList (https://developer.mozilla.org/en-US/docs/Web/API/Element/classList), so it never be "selected". Use the contains method instead:
function reset (){
if(easyMode.classList.contains("selected")){
easyMode();
} else if(hardMode.classList.contains("selected")) {
hardMode();
}
}
I hope this will help!
I am assuming you have a DOM tree that containing something like this
<button class='mode'>Easy</button>
<button class='mode'>Hard</button>
The code might be more readable if you explicitly named the buttons, rather than using an implicit array modeButtons[i] for your difficulty scale.
<button class='mode mode-easy'>Easy</button>
<button class='mode mode-hard'>Hard</button>
Your reset() function is trying to reference the prototype object chain of the functions easyMode.__proto__.classList and hardMode.__proto__.classList rather than the state of the DOM button. Also note that .classList is an array and not a string, so needs .contains()
function reset(){
if( modeButtons[0].classList.contains("selected") ) {
easyMode();
} else if( modeButtons[1].classList.contains("selected") ) {
hardMode();
}
}

Make a function only run once (Javascript)

So I'm trying to create a function which changes the 0 slot of an array to an input value. However I want this function to only be able to run once. How do I do this, any help is greatly appreciated thanks.
function submitForm() {
$('#inputForm').submit;
if ($("#inputValue").val() != inputException) {
charSetName();
$("#inputValue").val("");
}
$("#inputValue").val("");
}
function charSetName() {
var nameSet = false;
if (nameSet == false) {
nameSet = true;
charDetails[0] = $("#inputValue").val();
document.getElementById("name").innerHTML = "<li id='name''>Name: " + charDetails[0] + "</li>";
}
}
This is an old question but I was brought here because I needed something similar.
To anyone who comes next time, here was what I eventually did:
I simply declared a variable with an initial count of 0.
Then every time my function runs, it auto increases.
The code I want to run once just checks if the count is 1, then it runs. If not, it doesnt.
Like this:
let count = 0;
count === 1 ? doSomething : doNothing
count++
Does it help?
Try this, I have defined isFunctionCalled:Boolean variable for handle it.
var isFunctionCalled = false;
function submitForm() {
$('#inputForm').submit;
if ($("#inputValue").val() != inputException) {
if (!isFunctionCalled) {
charSetName();
}
$("#inputValue").val("");
}
$("#inputValue").val("");
}
function charSetName() {
var nameSet = false;
if (nameSet == false) {
nameSet = true;
charDetails[0] = $("#inputValue").val();
document.getElementById("name").innerHTML = "<li id='name''>Name: " + charDetails[0] + "</li>";
}
isFunctionCalled = true;
}
Mind the executed variable:
var something = (function() {
var executed = false;
return function () {
if (!executed) {
executed = true;
// do something
}
};
})();
There are a lot of ways to do this.
Just a simple way is to set a flag value.
if(flag){
//your code here...
flag = !flag;
}
SO, if the value of the flag is 1 at first it will work. Then as the flag value is changed to 0, then the next time it is called, it will not be invoked, as flag is 0.

Switch between Javascript functions based on Checkbox or Toggle selection (Tableau API)

I want to use a checkbox or toggle checkbox to switch between two Tableau Javascript API functions.
It get mixed results. A single filter will ADD and REMOVE in a checkbox. It will not work when applying multiple values to the filter (e.g., (filterName, [Value 1, Value 2], ADD)).
It will run the first action when "connected" to a checkbox toggle switch, but it will not switch back and return the other function.
function filterMainViz(filterName, values) {
var sheet = mainViz.getWorkbook().getActiveSheet();
var updateType;
if($('#'+values).is(":checked")) {
updateType = "ADD";
} else {
updateType = "REMOVE";
}
worksheetArray = sheet.getWorksheets();
for (var i = 0; i < worksheetArray.length; i++) {
worksheetArray[i].applyFilterAsync(filterName, values, updateType);
}
}
Similar problem when I try to apply this approach to switch between two functions. It will run the first action (check or toggle), but it will not switch back or run the second function.
function switchMainViz() {
var sheet = mainViz.getWorkbook().getActiveSheet();
var updateType;
if($('#').is(":checked")) {
chartPercent();
} else {
chartAmount();
}
}
The original chartPerce() is
function chartPercent() {
mainWorkbook = mainViz.getWorkbook();
// Set to Detail to Percent
mainWorkbook.changeParameterValueAsync("Amount or Percent", "Percent").then(function () {
alertOrConsole("'Chart Type' parameter set to Percent");
});
}
The original chartAmount() is
function chartAmount() {
mainWorkbook = mainViz.getWorkbook();
// Set to Detail to Amount
mainWorkbook.changeParameterValueAsync("Amount or Percent", "Amount").then(function () {
alertOrConsole("'Chart type' parameter set to Amount");
});
}
You are missing Id in your jquery selector right here if($('#').is(":checked")) in the function switchMainViz()
It should be like if($('#'+YourId).is(":checked"));

Changing Scope from Global to Local Breaking Javascript Program

Thanks to the help of you fine Overflowians, I fixed up my silly little RNG Addition game and got it working. Now, at one user's suggestion, I'm trying to change the scope of the addition game's code from global to local before I code up the next game; I want each game to be completely contained within its own scope, as I understand that learning to not thoughtlessly contaminate the global scope is a good idea. However, I'm a bit stuck on how to achieve that.
Here's the code for the currently functional addition game:
function beginAdditionChallenge() {
var x = Math.ceil(Math.random()*100);
alert(x);
for (var i = 0; i < 3; i++) {
var a = Number(prompt("Provide the first addend.", ""));
var b = Number(prompt("Provide the second addend.", ""));
if (a + b === x) {
alert("Well done!");
break;
}
else if (a + b !== x && i < 2) {
alert("Please try again.");
}
else {
alert("Derp.");
}
}
}
function initChallenge() {
var button = document.getElementById("challengeButton");
button.addEventListener("click", beginAdditionChallenge);
}
window.addEventListener("load", initChallenge);
And here's my attempt to wrap it, which only succeeds in breaking the game, such that the button doesn't even respond:
window.addEventListener("load", function() {
function beginAdditionChallenge() {
var x = Math.ceil(Math.random()*100);
alert(x);
for (var i = 0; i < 3; i++) {
var a = Number(prompt("Provide the first addend.", ""));
var b = Number(prompt("Provide the second addend.", ""));
if (a + b === x) {
alert("Well done!");
break;
}
else if (a + b !== x && i < 2) {
alert("Please try again.");
}
else {
alert("Derp.");
}
}
}
function initChallenge() {
var button = document.getElementById("challengeButton");
button.addEventListener("click", beginAdditionChallenge);
}
window.addEventListener("load", initChallenge);
});
Functional code is available on JSFiddle here.
Note that the onLoad option in JSFiddle does the same as your 2nd snippet. You'll want to choose one of the No wrap options when binding to 'load' yourself.
And, the issue stems from attempting to bind to 'load' within a 'load' handler:
window.addEventListener("load", function() {
// ...
window.addEventListener("load", initChallenge);
});
When the event is already firing and handling the outer binding, it's too late to add more handlers to it. They won't be cycled through and the event shouldn't occur again.
You'll either want to call initChallenge within the outer event binding:
window.addEventListener("load", function() {
// ...
initChallenge();
});
Or, you can use an IIFE with the inner binding:
(function () {
// ...
window.addEventListener("load", initChallenge);
})();

Using variable outside of jquery.click function?

I want to call a local variable, totalYes, outside of the jquery .click function. To do this I should make it a global variable, right? Originally, the code looked like:
var answers = [thing1, thing2, thing3, thing4, thing5, thing6, thing7, thing8, thing9, thing10, thing11, thing12];
var answers2 = [answers2array12items];
$("#submmit").click(function() {
var totalYes=0;
function checkAnswers() {
for(var i=0; i<answers.length; i++) {
var userAnswer = document.getElementById("b"+i).value.toLowerCase();
if (answers.indexOf(userAnswer.toLowerCase()) !== -1 || answers2.indexOf(userAnswer.toLowerCase()) !== -1) {
totalYes++;
$("#correcto").show();
} else {
$("#incorrecto").show();
}
}
}
checkAnswers();
alert(totalYes);
});
And it works fine, however, I want to use it in:
$("total").click(function(){
alert(totalYes);
});
So I took totalYes, and make it "global", outside the function. The new version is:
var answers = [thing1, thing2, thing3, thing4, thing5, thing6, thing7, thing8, thing9, thing10, thing11, thing12];
var answers2 = [answers2array12items];
var totalYes = 0;
$("#submmit").click(function() {
function checkAnswers() {
for(var i=0; i<answers.length; i++) {
var userAnswer = document.getElementById("b"+i).value.toLowerCase();
if (answers.indexOf(userAnswer.toLowerCase()) !== -1 || answers2.indexOf(userAnswer.toLowerCase()) !== -1) {
totalYes++;
$("#correcto").show();
} else {
$("#incorrecto").show();
}
}
}
checkAnswers();
alert(totalYes);
});
But in the new code, instead of adding 1 to totalYes for every correct answer, and increasing totalYes like this: "1,2,3,4,5,6,7,8,9,10,11,12", it increases as: "1,3,6,10,15,21,27,33,39,46,54". I have tried changing the totalYes modifier inside of checkAnswers(), from totalYes++; to totalYes+=1;, but the problem persists.
Also, I would like to know, why the alert box shows up twice every time, instead of just once?
edit: here's the html and css for #total and #submmit:
<div class="nextsa1" id="total"><strong>TOTAL</strong></div>
<input type="button" id="submmit" value="GO">
Your code Appears to search the complete answer List each Time it is called upon the user entering a new answer. Therefore, on each invocation the Test Inside your Click handler is successful for the current and all previous answers, which explains the Delta pattern of the totalYes variable.
Remedy: init totalYes to 0 as the first Instruction inside the Click Handler.

Categories

Resources