JS Variable Scope and class change - javascript

I have this small little app that I built but I was wondering if there was a way to make this code more condensed/easier
var i = -1;
$( "#next, #back, #remove" ).click(function () {
var filters = [ "aden", "reyes", "perpetua", "inkwell",
"toaster", "walden", "hudson",
"gingham", "mayfair", "lofi",
"xpro2", "1977", "brooklyn"]
function changefilter(){
$('figure').removeClass().addClass(filters[i]);
$('h1').text(filters[i]);
console.log(filters[i]);
}
if (this.id == 'next' && i < filters.length) {
i++;
changefilter();
} else if (this.id == 'back' && i > -1 && i !== undefined) {
i--;
changefilter();} else if (this.id == 'remove'){
i = -1;
changefilter();
}
});
basically the app has 3 buttons which will cycle the class on the figure element.
I am still really new to javascript and I know this would have to do something with the scope but why is it that if I put the "i" variable at the very top (inside) of my function
var i = -1;
$( "#next, #back, #remove" ).click(function () {
var filters = [ "aden", "reyes", "perpetua", "inkwell",
"toaster", "walden", "hudson",
"gingham", "mayfair", "lofi",
"xpro2", "1977", "brooklyn"]
the counter does not move? Also, i put "i" to be -1 because i didn't want to have a class applied until the button was hit. Is this a good way to do this? I also noticed that when pressing the remove button the title stays at the most recent selection and does not revert back to the -1 option
Is there a way to have a variable that all functions, within a function, are able to see without making it global? Is there even a point to doing that? Am I even making sense anymore?
Thanks everyone!
If you want to see this i have a pen at http://codepen.io/Oreilly617/pen/KdrOom

var i = -1;
$( "#next, #back, #remove" ).click(function () {
var filters = [ "aden", "reyes", "perpetua", "inkwell", "toaster", "walden",
"hudson", "gingham", "mayfair", "lofi", "xpro2", "1977", "brooklyn"];
switch (this.id) {
case 'next':
changefilter(++i);
break;
case 'back':
changefilter(--i);
break;
case 'remove':
i = -1;
$('h1').text('Fun Filter');
break;
}
function changefilter(i){
var length = filters.length;
var figure = $('figure');
var current_class = figure.prop('class');
if (i >= 0 && $.inArray(current_class) < (length - 1)) {
figure.removeClass(current_class);
$('figure').addClass(filters[i])
$('h1').text(filters[i]);
}
}
});

I am not sure this meets your "more condensed" but it does resolve the issue of variable scope and demonstrates creation and use of an object to contain functions etc. Some of this is still a bit verbose but left that way for clarity. Note that this also "cycles" through your list and returns back to the base when the next/back reach the end of the cycle as well.
Here is a fiddle if you wish to play around with it: http://jsfiddle.net/MarkSchultheiss/9vzy3rLm/1/
var mything = {
basei: -1,
i: this.basei,
figureSelector: 'figure.lens',
defaultDisplay: 'Fun Filterc',
filters: ["aden", "reyes", "perpetua", "inkwell",
"toaster", "walden", "hudson",
"gingham", "mayfair", "lofi",
"xpro2", "1977", "brooklyn"],
maxFilter: 0,
maxFilterCalc: function () {
this.maxFilter = this.filters.length - 1;
},
changefilter: function changefilter() {
$(this.figureSelector).removeClass(this.filters.join(' ')).addClass(this.filters[this.i]);
if (this.i == -1) {
$('h1').text(this.defaultDisplay);
} else {
$('h1').text(this.filters[this.i]);
}
},
processButton: function (me) {
this.maxFilterCalc();
if (me.id == 'next') {
if (this.i < this.maxFilter) {
this.i++;
} else {
this.i = this.basei;
}
} else if (me.id == 'back') {
if (this.i > this.basei) {
this.i--;
} else {
this.i = this.maxFilter;
}
} else if (me.id == 'remove') {
this.i = this.basei;
}
this.changefilter();
}
};
$("#next, #back, #remove").click(function () {
mything.processButton(this);
});

Related

Javascript/Jquery toggle though objects with event

I have an aframe-scene with two arrows and some objects and i try to toggle through the objects, but i dont get it right some help would be nice here what i got:
AFRAME.registerComponent("fool", {
init: function() {
var boxarray = ["#bluebox, #yellowbox, #greenbox]
boxarray[0] = true;
if(boxarray[0] = true){
document.getElementById('bluebox').setAttribute('visible', 'true');
document.getElementById('bluebox').setAttribute('scale', {x:1,y:1,z:1});
} else {
document.getElementById('bluebox').setAttribute('visible', 'false');
document.getElementById('bluebox').setAttribute('scale', {x:0,y:0,z:0});
}
if(boxarray[1] = true){
document.getElementById('bluebox').setAttribute('visible', 'true');
document.getElementById('bluebox').setAttribute('scale', {x:1,y:1,z:1});
} else {
document.getElementById('bluebox').setAttribute('visible', 'false');
document.getElementById('bluebox').setAttribute('scale', {x:0,y:0,z:0});
}
if(boxarray[2] = true){
document.getElementById('bluebox').setAttribute('visible', 'true');
document.getElementById('bluebox').setAttribute('scale', {x:1,y:1,z:1});
} else {
document.getElementById('bluebox').setAttribute('visible', 'false');
document.getElementById('bluebox').setAttribute('scale', {x:0,y:0,z:0});
}
function toggleright(){
?help?
}
function toggleleft(){
?help?
}
})
I try to give all of my objects(boxes) an event so changing source is just useful if the event changes too
How about having one method which will toggle the "next" entity visible, and the "previous" one invisible.
This way, your left / right method will only determine which one should be next and use the toggling function.
It could be done with a component like this:
AFRAME.registerComponent("foo", {
init: function() {
this.objects = ["one", "two", "three"]
this.iterator = 0
this.left = AFRAME.utils.bind(this.left, this);
this.right = AFRAME.utils.bind(this.right, this);
},
right: function() {
let i = (this.iterator - 1) < 0 ? this.objects.length - 1 : this.iterator - 1
this.toggle(this.iterator, i)
},
left: function() {
let i = (this.iterator + 1) >= this.objects.length ? 0 : this.iterator + 1
this.toggle(this.iterator, i)
},
toggle: function(oldEl, newEl) {
document.getElementById(this.objects[oldEl]).setAttribute("visible", "false")
document.getElementById(this.objects[newEl]).setAttribute("visible", "true")
this.iterator = newEl
}
})
The right and left methods only check if there we didn't reach the beginning / end of the array, and call the toggle method which switches the visibility.
Live fiddle here.

How to change cases in a switch statement once one case condition is met?

I'm trying to figure out how to switch between cases in Javascript/jQuery. I'm creating a dice game where the player needs to roll the dice until they finish the "Phase". Once they finish the "Phase" they will move onto the next "Phase" and any previous "Phases" won't interfere with the current "Phase". There will be several different "Phases" where they start at "Phase 1" and work down to "Phase 2" and so on until the last phase. Is there any way I can work down the "Phases"? I'm trying a switch statement here with no success. If a switch statement won't do the job, what will?
//Start of Dice Code
var die1Array = [];
var die2Array = [];
var die3Array = [];
var die1 = function() {
var roll1 = Math.floor(Math.random() * 6) + 1;
if(roll1 === 1) {
die1Array.push(1);
}
else if(roll1 === 2) {
die1Array.push(2);
}
else if(roll1 === 3) {
die1Array.push(3);
}
else if(roll1 === 4) {
die1Array.push(4);
}
else if(roll1 === 5) {
die1Array.push(5);
}
else if(roll1 === 6) {
die1Array.push(6);
}
};
var die2 = function() {
var roll2 = Math.floor(Math.random() * 6) + 1;
if(roll2 === 1) {
die2Array.push(1);
}
else if(roll2 === 2) {
die2Array.push(2);
}
else if(roll2 === 3) {
die2Array.push(3);
}
else if(roll2 === 4) {
die2Array.push(4);
}
else if(roll2 === 5) {
die2Array.push(5);
}
else if(roll2 === 6) {
die2Array.push(6);
}
};
var die3 = function() {
var roll3 = Math.floor(Math.random() * 6) + 1;
if(roll3 === 1) {
die3Array.push(1);
}
else if(roll3 === 2) {
die3Array.push(2);
}
else if(roll3 === 3) {
die3Array.push(3);
}
else if(roll3 === 4) {
die3Array.push(4);
}
else if(roll3 === 5) {
die3Array.push(5);
}
else if(roll3 === 6) {
die3Array.push(6);
}
};
//End of Dice Code
var main = function() {
$("#roll").on("click", die1);
$("#roll").on("click", die2);
$("#roll").on("click", die3);
$("#roll").on("click", die4);
$("#roll").on("click", die5);
$("#roll").on("click", die6);
//Where I want to switch between cases.
//Once Phase 1's condition (the if statement) is met, I want to switch to Phase 2.
var lvls = 1;
switch(lvls) {
case 1:
alert("Phase 1");
$("#submit").click(function() {
if((die1Array.slice(-1)=="1"||die2Array.slice(-1)=="1"||die3Array.slice(-1)=="1")&&(die1Array.slice(-1)=="2"||die2Array.slice(-1)=="2"||die3Array.slice(-1)=="2")&&(die1Array.slice(-1)=="3"||die2Array.slice(-1)=="3"||die3Array.slice(-1)=="3")) {
alert("Completed Phase 1: Straight of 3");
lvls = 2;
return lvls;
}
else {
alert("Phase 1: Straight of 3. Not Complete. Try again.");
};
});
break;
case 2:
alert("Phase 2");
//Phase 2's code
break;
//Additional cases/Phases would go here.
default:
};
};
$(document).ready(main);
In your die# function you could just push the values without the if else part.
var die1 = function () {
var roll1 = Math.floor(Math.random() * 6) + 1;
die1Array.push(roll1);
}
Or more general:
var die = function (dieArray) {
dieArray.push(Math.floor(Math.random() * 6) + 1);
}
Using:
die(die1Array);
die(die2Array);
die(die3Array);
...
The issue of your current swithc is that you bind an event handler function inside a case.
If you want that event handler work only in a particular state, you should set a state variable to save the current state and check this variable in the handler function.
var main = function () {
var diceState = 1;
$("#submit").click(function() {
if (diceState === 1) {
// do something
}
});
switch(lvls) {
case 1:
diceState = 1;
break;
case 2:
diceState = 2;
break;
}
The other problem you have is that you set the value of lvls to 1 in the main function and so you will have only 1 case, the other changes of lvls will have no effect at all, even you call main again.
If you call main more than one time, then you have the problem you're continuosly binding event onsubmit, so each time you submit the form, the function handler is called many times.
You should move the switch statement in a different function, passing the state variable as parameter, and call each time you want to change the state.
So here an example:
// Should be in same scope of the function
// or an attribute of an object to be passed by reference.
var diceState;
var changeState = function (state) {
switch(state) {
case 1:
diceState = 1;
break;
....
default:
throw new Error("State " + state + " not supported");
}
}
And is a good practice to use the default case to handle unexpected values.
Here a suggestion on how to change your main function:
var main = function() {
var lvls;
$("#roll").on("click", die1);
$("#roll").on("click", die2);
$("#roll").on("click", die3);
$("#roll").on("click", die4);
$("#roll").on("click", die5);
$("#roll").on("click", die6);
var changeState = function (state) {
switch(state) {
case 1:
alert("Phase 1");
lvls = state;
break;
case 2:
alert("Phase 2");
//Phase 2's code
lvls = state;
break;
//Additional cases/Phases would go here.
default:
};
}
// bind the event handler
$("#submit").click(function() {
if((die1Array.slice(-1)=="1"||die2Array.slice(-1)=="1"||die3Array.slice(-1)=="1")&&(die1Array.slice(-1)=="2"||die2Array.slice(-1)=="2"||die3Array.slice(-1)=="2")&&(die1Array.slice(-1)=="3"||die2Array.slice(-1)=="3"||die3Array.slice(-1)=="3")) {
alert("Completed Phase 1: Straight of 3");
changeState(2);
// you could not return the vaue here!
// return lvls;
}
else {
alert("Phase 1: Straight of 3. Not Complete. Try again.");
};
});
changeState(1);
};
Before addressing your question I want to mention your if/else statements:
if(roll1 === 1) {
die1Array.push(1);
}
else if(roll1 === 2) {
die1Array.push(2);
}
If you're going to put the value of roll1 into die1Array regardless of what the value is, why are you checking it first every time? Just do a die1Array.push(roll1); and call it a day.
I don't think you even want a switch statement here at all. What are you hoping to achieve with a switch statement?
The logic of your code is essentially this:
var main = function() {
// onclick stuff
lvls = 1
switch(lvls){}
}
This is going to immediately go into this switch statement with the hard value of 1 you force immediately before it, thus always hitting the first case.
The logic of this seems like it would be better suited to calling various functions. Maybe a function for phaseTwo(){...} but a switch statement is primarily used to check the state of an item at a specific point, not for tracking the state over time.
Here is some working ES6 code you may find useful.
It defines the names and the verification functions for each of the phases in an array. The roll function performs the random generation of the 5 dices (could be any number) a few times, so to give some animation, and then calls back, providing the value of each of the dice.
A global lvl variable is then used to find and execute the correct verification function. If this passes, the lvl is incremented:
const dieFaces = [null,'⚀','⚁','⚂','⚃','⚄','⚅'];
const DICE_COUNT = 5;
var lvl = 0;
var phases = [
{
descr: 'Phase 1: Pair',
test: function (counts) {
return counts.some( count => count > 1 );
}
},
{
descr: 'Phase 2: Three of a Kind',
test: function (counts) {
return counts.some( count => count > 2 );
}
},
{
descr: 'Phase 3: Full House',
test: function (counts) {
return counts.some( count => count > 2 ) &&
counts.filter( count => count > 1 ).length > 1;
}
},
{
descr: 'Phase 4: Small Straight',
test: function (counts) {
return counts.map( count => count>0 ).join('').indexOf('1111') > -1;
}
},
{
descr: 'Phase 5: Large Straight',
test: function (counts) {
return counts.map( count => count>0 ).join('').indexOf('11111') > -1;
}
},
{
descr: 'Phase 6: Four of a Kind',
test: function (counts) {
return counts.some( count => count > 3 );
}
}
];
function roll(callback, rolling = 20) {
let faces = [];
let dice = [];
for (let i = 0; i < DICE_COUNT; i++) {
let die = Math.floor(Math.random() * 6) + 1;
dice.push(die);
faces.push(dieFaces[die]);
}
$('#dice').html(faces.join(' '));
if (!rolling) return callback(dice);
setTimeout(roll.bind(null, callback, rolling-1), 50);
}
$("#roll").on("click", function () {
$('#result').text("");
roll(function (dice) {
var acc = dice.map( die => 0 );
dice.forEach( die => ++acc[die] );
if (phases[lvl].test(acc)) {
$('#result').text("Completed " + phases[lvl].descr);
lvl++;
$('#phase').text(phases[lvl].descr);
} else {
$('#result').text("Try again");
}
});
});
$('#phase').text(phases[0].descr);
#dice { font-size: 40px }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="phase"></div>
<button id="roll">Roll</button>
<div id="dice"></div>
<div id="result"></div>

Jquery : swap two value and change style

i need to make a script for select a black div by click(go red), and put black div value into a white div value by another click, this is ok but when i try to swap values of two white case, the change do correctly one time, but if i retry to swap two value of white case the values swap correctly but whitout the background color red.
This is my code :
var lastClicked = '';
var lastClicked2 = '';
$(".blackcase").click(function(e) {
var i = 0;
if ($(this).html().length == 0) {
return false;
} else {
e.preventDefault();
$('.blackcase').removeClass('red');
if (lastClicked != this.id) {
$(this).addClass('red');
var currentId = $(this).attr('id');
var currentVal = $(this).html();
$(".whitecase").click(function(e) {
$('.blackcase').removeClass('red');
var currentId2 = $(this).attr('id');
if (i <= 0 && $("#" + currentId2).html().length == 0) {
$("#" + currentId2).html(currentVal);
$("#" + currentId).html("");
i = 1;
}
});
} else {
lastClicked = this.id;
}
}
});
$(".whitecase").click(function(e) {
var j = 0;
if ($(this).html().length == 0) {
return false;
} else {
e.preventDefault();
$('.whitecase').removeClass('red');
if (lastClicked2 != this.id) {
$(this).addClass('red');
var currentId0 = $(this).attr('id');
var currentVal0 = $(this).html();
$(".whitecase").click(function(e) {
e.preventDefault();
var currentId02 = $(this).attr('id');
var currentVal02 = $(this).html();
if (j <= 0 && currentVal0 != currentVal02) {
$('.whitecase').removeClass('red');
$("#" + currentId02).html(currentVal0);
$("#" + currentId0).html(currentVal02);
j = 1;
return false;
}
});
} else {
lastClicked2 = this.id;
}
}
});
This is JSfiddle :
https://jsfiddle.net/12gwq95u/12/
Try to take 12 and put into first white case, put 39 into second white case, click on the white case with 12 (go red) then click on the white case with 39, the values swap correctly with the red color when it's select, but if you try to reswap two whitecase values thats work but without the red color.
Thanks a lot
I have spent some time to rewrite your code to make it more clear. I don't know what exactly your code should do but according to the information you have already provided, my version of your code is the following:
var selectedCase = {color: "", id: ""};
function removeSelectionWithRed() {
$('div').removeClass('red');
}
function selectWithRed(element) {
removeSelectionWithRed();
element.addClass('red');
}
function updateSelectedCase(color, id) {
selectedCase.color = color;
selectedCase.id = id;
}
function moveValueFromTo(elemFrom, elemTo) {
elemTo.html(elemFrom.html());
setValueToElem("", elemFrom);
}
function setValueToElem(value, elem) {
elem.html(value);
}
function swapValuesFromTo(elemFrom, elemTo) {
var fromValue = elemFrom.html();
var toValue = elemTo.html();
setValueToElem(fromValue, elemTo);
setValueToElem(toValue, elemFrom);
}
function isSelected(color) {
return selectedCase.color == color;
}
function clearSelectedCase() {
selectedCase.color = "";
selectedCase.id = "";
}
function elemIsEmpty(elem) {
return elem.html().length == 0;
}
$(".blackcase").click(function (e) {
if (elemIsEmpty($(this))) {
return;
}
alert("black is selected");
selectWithRed($(this));
updateSelectedCase("black", $(this).attr("id"), $(this).html());
});
$(".whitecase").click(function (e) {
removeSelectionWithRed();
if (isSelected("black")) {
alert("moving black to white");
moveValueFromTo($("#"+selectedCase.id), $(this));
clearSelectedCase();
return;
}
if(isSelected("white") && selectedCase.id !== $(this).attr("id")) {
alert("swap whitecase values");
swapValuesFromTo($("#"+selectedCase.id), $(this));
clearSelectedCase();
return;
}
alert("white is selected");
selectWithRed($(this));
updateSelectedCase("white", $(this).attr("id"), $(this).html());
});
Link to jsfiddle: https://jsfiddle.net/12gwq95u/21/
If my answers were helpful, please up them.
It happens because you have multiple $(".whitecase").click() handlers and they don't override each other but instead they all execute in the order in which they were bound.
I advise you to debug your code in browser console by setting breakpoints in every click() event you have (in browser console you can find your file by navigating to the Sources tab and then (index) file in the first folder in fiddle.jshell.net).
In general I think you should rewrite you code in such a way that you won't have multiple handlers to the same events and you can be absolutely sure what your code does.

How to use javascript conditions correctly?

I have a slider function, which works perfectly fine with moveRight() but I am stuck, and can use it only once.
I decided to use a condition to disable the move function and change the attributes of links on the second click. My code below:
$('#control_next').click(function () {
var used = 0;
if (used == 0) {
moveRight();
used = 1;
} else if (used == 1) {
$('.control_next').attr('href', '#business-events');
$('.control_next').addClass( "url" );
}
alert(used);
});
You have to place the varaible used outside your event handler because else it will always be 0 with every click:
var used = 0;
$('#control_next').click(function () {
if (used == 0) {
moveRight();
used = 1;
}
else if (used == 1) {
$('.control_next').attr('href', '#business-events');
$('.control_next').addClass( "url" );
}
alert(used);
});
You should declare variable Globally. Like
var used = 0;
$('#control_next').click(function () {
if (used == 0) {
moveRight();
used = 1;
}
else if (used == 1) {
$('.control_next').attr('href', '#business-events');
$('.control_next').addClass( "url" );
}
});

unbind/turn off a function in jquery

Good day all, I'm trying to make a jquery game where a group of enemy will spawn after a group of enemy gets destroyed. I'm calling alien_cruiser() function & unbinding minion_roulette() function after minion_roulette_counter gets 0. But every time I run, function does not get unbind & after counter gets 0 both type of enemies show. I want to run them one by one. Here are the codes:
var sound = new Audio("sounds//dishoom.ogg");
var score = 0;
var minion_roulette_life = 10;
var cruiser_life = 20;
var minion_roulette_counter = 3;
var cruiser_counter = 3;
function processBullet() {
$(".projectile").each(function() {
var maxTop = $(this).offset().top;
var breakable1 = $(this).collision("#minion-roulette");
var breakable2 = $(this).collision("#cruiser");
$(this).css("top", maxTop - 25);
if (breakable1.length != 0 || breakable2.length != 0) {
$(this).remove();
}
if (maxTop <= 35) {
$(this).remove();
}
if (breakable1.length != 0) {
--minion_roulette_life;
if (minion_roulette_life == 0) {
sound.play();
breakable1.remove();
minion_roulette(true);
minion_roulette_counter--;
$("#score").html(++score);
minion_roulette_life = 10;
}
}
//This is the place where it checks if counter is 0 or not
if (minion_roulette_counter == 0) {
$('#content').unbind(function() {
minion_roulette(false)
});
alien_cruiser(false);
minion_roulette_counter = -1;
}
if (breakable2.length != 0) {
--cruiser_life;
if (cruiser_life == 0) {
sound.play();
breakable2.remove();
alien_cruiser(true);
$("#score").html(++score);
cruiser_life = 20;
}
}
});
}
Am I doing any wrong here? Please I need a solution badly. Tnx.
In this situation, you could use a conditional statement to determine which function to call.
For example:
if (minion_roulette_counter == 0) {
alien_cruiser();
}
else {
minion_roulette();
}
Binding and unbinding doesn't 'turn off' a function, unfortunately. To quote MDN:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
– MDN: 'Bind'

Categories

Resources