I am trying to use the following switch statement in order to change a global variable, but it will only change once.
HTML
<button type="button" value =15>16x16</button>
<button type="button" value =31>32x32</button>
<button type="button" value =63>64x64</button>
JavaScript
var initial_size = 15;
var $cl;
var $size;
$(document).ready(function(){
populate(initial_size);
});
function populate(size){
$('.main-table').empty();
$size = size;
switch($size){
case 15:
$cl = 'box';
break;
case 31:
$cl = 'bigger-box';
console.log("case 31");
break;
case 63:
$cl = 'biggest-box';
console.log("case 63");
break;
default:
break;
}
console.log($cl);
for(var i=0; i < $size; i++){
$tr = $("<tr></tr>");
for(var j=0; j < $size; j++){
$div = $("<div class=" +$cl+"></div>");
$div.css('background-color', 'lightslategrey');
$tr.append($("<td></td>").append($div));
}
$(".main-table").append($tr);
}
run();
};
function run(){
$('.box').hover(function(){
$(this).css('background-color', 'black');
});
$('button').click(function(){
console.log($(this).val());
populate($(this).val());
});
};
So when I run my code, $cl will be set to 'box', but whenever I click on another button and the loop runs again, $cl does not change to any of the other classes. Am I using the switch statement wrong, or am I not parsing the value correctly, or is the problem because I am using global variables? The only reason I am using global variables is because I am not too familiar with how scoping works in javascript yet, so I figured this way might be easier.
Am I using the switch statement wrong, or am I not parsing the value correctly?
Exactly this. The values of your buttons are strings, but your switch statement is operating on numbers. case statements use strict equality which includes the type. So either parse your values to numbers, or use string literals in your cases. Currently, your code will always reach the default case.
The only reason I am using global variables is because I am not too familiar with how scoping works in javascript yet, so I figured this way might be easier.
Quite simple actually: All function declarations, parameter declarations, and var declarations are scoped to the function that contains them. You really should be using local variables here.
Btw, you really should move that run(); invocation from populate into the document-ready callback, next to populate(initial_size);. Currently, you are attaching a new set of event listeners every time you click a button, and all those listeners will fire on each click.
Related
WHAT I WANT TO HAPPEN
So what I want to happen is function partA() to click button [z] every 2 seconds. The button that is being clicked should change, because the script is a looping script, so for instance. The first loop, it would click button 1, then button 2, then button 3, because var z = 1 + i++. Is this possible? z is supposed to equal the number 1, plus the loop number. This should loop as long as variable i is less than 50.
WHAT IS HAPPENING
It works properly, looping and all, if I test the script without variable z. However, without variable z, I have to manually change the number that would equal z, which is painstaking, and annoying.
var z = 1 + i++
for(i=0;i<50;i++) {
setInterval(partA, 2000);
function partA() {
buttons = document.getElementsByTagName('button');
document.getElementsByTagName('button')[z].click();
}
}
Should i++ be defined another way? The error I'm getting when running the script in console is:
Uncaught ReferenceError: i is not defined (...)
at :2:13
at Object.InjectedScript._evaluateOn (:878:140)
at Object.InjectedScript._evaluateAndWrap (:811:34)
at Object.InjectedScript.evaluate (:667:21)
There's a couple of suggestions I could advise with your code so I'll try and address each one individually.
Firstly define your function outside of your loop. If you would like to know the reasons behind this please read: Don't make functions within a loop
Secondly you should really declare i as a variable to set the scope to which it applies. Some good information on this is at: Declaring variables without var keyword
Thirdly when you run your loop you could run the code inside an IIFE. The reason for this is when you run setInterval, by the time it runs i will actually be 3 (or the last number of your loop). This is due to the asynchronous nature of setInterval, and that the reference to i is bound to the function, not the value of i.
Example
for(var i=0;i<3;i++) {
(function(i) {
setInterval(clickButton(i), 2000);
})(i)
}
function clickButton(idx) {
return function() {
buttons = document.getElementsByTagName('button');
document.getElementsByTagName('button')[idx].click();
}
}
JSBin Demo
http://jsbin.com/harigewuze/edit?html,js,output
Why are you trying to define z outside the loop? Just use i.
for (var i = 0; i < 50; i++) {
...
document.getElementsByTagName('button')[i].click();
}
without changing your code too much I would write it like this...
you know its looping 50 times, you know i is incrementing from 0 to 49, use i to change the button name and you don't need z...
for(i=0;i<50;i++) {
setInterval(partA, 2000);
function partA() {
buttons = document.getElementsByTagName('button');
document.getElementsByTagName('button')[i + 1].click();
}
}
1) This is how you want your code to look like :
var z;
for(i=0;i<50;i++) {
z=i;
setInterval(partA, 2000);
}
function partA() {
buttons = document.getElementsByTagName('button');
document.getElementsByTagName('button')[z].click();
}
2) Unfortunately, in javascript you have a problem with this code due to the fact of scopes. My recommendation is to read this link first http://www.mennovanslooten.nl/blog/post/62 and understand how it works.
If you did understand it, then thumb up..you just promoted yourself to a higher level in javascript ;)
3) If you are still having issues, post it on JSFiddle
I am feeling a bit embarrassed to ask this silly question but somehow the following code is not working. The values (bed,room,floor) are fetched right but switch case not working.But the same snippet of switch case code works well in jsfiddle.I m sure I m missing something silly.I m using this in socialengine with mootools enabled
I also want to know how to get an element which has id=roomtype and which is inside a div whose class=form-elements but id=roomtype is not direct child of div class=form-elements.
I can get it by $$('.form-elements #roomtype').get('value') but this refers all elements with #roomtype, this $('.form-elements #roomtype').get('value') doesnt work
<script type="text/javascript">
var updatePrice=function()
{
var room= $$('.form-elements #roomtype').get('value') ;
// alert (room) gets AirCon or Dorm
var price;
switch (room)
{
case "AirCon":
price="10000"; alert("AirCon");
break;
case 'Dorm':
price="5000"; alert("Dorm");
break;
default:
price="00";
}
}
en4.core.runonce.add(updatePrice);// this add this function to DOM Ready
</script>
#1
ID's must be unique.
In MooTools you can do $('roomtype') to get the element with the ID roomtype.
You can also use document.id('roomtype') or even $$('#roomtype').
Since ID's must be unique it's irrelevant the parent of that element, because there is only one.
#2
Note that $ and $$ are different element methods in mootools. $$ takes a CSS selector and returns a collection/array.
$$('#roomtype').get('value'); // returns ['the value']
$('roomtype').get('value'); // return 'the value'
#3
The other problem I see is that you define the price inside the scope of updatePrice() function, so doing price = 5000 inside the switch will not leave the function's scope anyway. You might want to define the var price outside the function, or make the function return that value lie this:
// ...
switch (room) {
case "AirCon":
price = "10000";
alert("AirCon");
break;
case 'Dorm':
price = "5000";
alert("Dorm");
break;
default:
price = "00";
}
return price; // here
}
If you update your fiddle with HTML it will be easier to help you more.
P.s: You are always welcome to ask questions. No need to feel embarrassed.
this is my first post ever on stackoverflow! I am a front-end web developer enthusiast and novice...If I am breaching the stackoverflow etiquette or rules of the road please let me know or give me the smack down..
I am trying to evaluate class names in an array of elements. If the class name contains a certain value then I want to manipulate an attribute for that element.
First, I create an array of elements using jquery stored as a variable:
note that buttons class naming convention is "share_button_[social media service name]"
Next, I create a for loop to iterate through the buttons variable
Within the for loop I have switch statement - the purpose is to evaluate each element in the Buttons array and add an href attribute to the element if it meets a certain criteria
Putting it all together:
var buttons = $('a[class^="share_button"]');
for (i=0; i < buttons.length; i++) {
switch (true) {
case ($(buttons[i]).attr('[class*="twitter"]')):
console.log('twitter!');
break;
case ($(buttons[i]).attr('[class*="linkedin"]')):
console.log('linkedin!');
break;
case ($(buttons[i]).attr('[class*="facebook"]')):
console.log('facebook_like!');
break;
case ($(buttons[i]).attr('[class*="google_plusone"]')):
console.log('google_plusone!');
break;
case ($(buttons[i]).attr('[class*="reddit"]')):
console.log('reddit!');
break;
}
}
This does not seem to be working at all. Here is the codepen, http://cdpn.io/fKoak
Is it a good practice to loop through a jquery array of elements like this?
Should I be using the switch statement in this case and am I using it correctly? (there are more possible cases then I have case statements for and I have no default - I want the cases without a match to "do noting")
In this particular case, what i wrong with the formation of my code that the desired outcome is not happening?
I think it would be better to do something more like this.
var $buttons = $('a[class^="share_button"]');
var $twitterButtons = $('[class*="twitter"]', $buttons);
$twitterButtons.each(function(i, button) {
//Do stuff to the twitter button
});
var $linkedinButtons = $('[class*="linkedin"]', $buttons);
$linkedinButtons.each(function(i, button) {
//Do stuff to the linkedin button
});
var $facebookButtons = $('[class*="facebook"]', $buttons);
$facebookButtons.each(function(i, button) {
//Do stuff to the facebook button
});
var $google_plusoneButtons = $('[class*="google_plusone"]', $buttons);
$google_plusoneButtons.each(function(i, button) {
//Do stuff to the google_plusone button
});
var $redditButtons = $('[class*="reddit"]', $buttons);
$redditButtons.each(function(i, button) {
//Do stuff to the reddit button
});
Adding the second parameter to your selectors gives them a context. So $('[class*="twitter"]', $buttons) looks through $buttons and selects those with a class containing twitter
You can use jQuery's each() method to iterate over the elements, then check the className
$('a[class^="share_button"]').each(function(i, elem) {
if ( elem.className.indexOf('twitter') != -1 ) {
console.log('twitter');
}else if ( elem.className.indexOf('linkedin') != -1 ) {
console.log('linkedin');
}else if (..... etc
});
A better approach would be to keep the switch, but google_plusone kinda screws that up with the underscore, so you'd have to replace that with something else:
$('a[class^="share_button"]').each(function(i, elem) {
switch( elem.className.split('_').pop() ) {
case 'twitter' :
console.log('twitter');
break;
case 'linkedin' :
console.log('linkedin');
break;
case 'googleplusone' :
// you'll have to remove the underscore or just check for "plusone"
}
});
I ultimately decided to drop the for loop and use jquery .each method -http://api.jquery.com/each/ - that was recommended by #adeno above. The two solutions offered by #adeno using .each both work good but I finally chose to go with the jquery .is method https://stackoverflow.com/a/2240085 - http://api.jquery.com/is/ - since we decided to use .each method it is already a "jquery solution" so using .is method to evaluate if the class name of each element contained a certain value was a lot less code - and allowed for more flexibility then the proposed .indexOf and .split/.pop methods by #adeno in imo..
#Tom also gave a workable solution. However, although I didn't mention it specifically in my question, I wanted a solution that would use an iterator to go through the array of selected button elements.
var $buttons = $('a[class^="share_button"]');
$buttons.each(function(i,e){
switch (true) {
case ($(e).is('[class*="twitter"]')):
alert('yea! it\'s a twitter button - now do something');
break;
case ($(e).is('[class*="linkedin"]')):
alert('yea! it\'s a linkedin button - now do something');
break;
case ($(e).is('[class*="facebook"]')):
alert('yea! it\'s a faceboook button - now do something');
break;
case ($(e).is('[class*="google_plusone"]')):
alert('yeah! it\'s a google plus one button - now do something');
break;
case ($(e).is('[class*="reddit"]')):
alert('yea! it\'s a reddit one button - now do something');
break;
}
});
I am currently building a small web application with similar functionality across all modules. I want to code small generic functions so that all programmers next to me, call these functions and these functions return necessary but important data for them to implement their functionality. In this example, I am trying to deal with the typical "choose true or false" exercise. So from the template.php they call this function:
function checkAnswers(){
var radiobuttons = document.form1.exer1;
var correctAnswers = answers(); //this is an array of string
var checkedAnswers = checkExerciseRB(radiobuttons, 2, correctAnswers);
for(i=0; i<checkedAnswers.length; i++){
alert(checkedAnswers[i]);
}
}
Function checkExerciseRB is my generic function, it is called from checkAnswers.
function checkExerciseRB(rbuttons, opciones, correct){
var answers = new Array();
var control = 0;
for(i=0; i<rbuttons.length; i++){
var noPick="true";
for(j=0; j<opciones; j++){
if(rbuttons[control+j].checked){
if(rbuttons[control+j].value==correct[i]){
answers[i]= 1;
noPick="false";
break;
}
else{
answers[i]=2;
noPick="false";
break;
}
}
}
if(noPick=="true")
answers[i]=0;
control=control+opciones;
}
return answers;
}
It works great but while looking at my favorite browsers (FireFox, Chrome) error log it says:
TypeError: rbuttons[control + j] is undefined
Any clue on how to deal with this matter?
This probably means that control + j is greater than or equal to the length of the array rbuttons. There's no such array element as rbuttons[control + j].
You should learn how to use the JavaScript debugger in your favorite browsers! Debuggers are great. They let you watch this code run, line by line, as fast or as slow as you want, and watch how the value of control changes as you go.
You’ll watch it, and you’ll think “Oh! That line of code is wrong!”
You're looping through rbuttons.length times, but in each loop you're adding 2 to control. Using control to index your array, you're going to run past the end.
Does the index specified by control + j exist in the array? i.e: If that evaluates to 4, is there at least 5 items in the array?
Also, you should be using var i, var j, etc inside your for loop. Without it your variables are leaking into the scope this code is executed in (most likely the global scope, and that's not good) :)
I am trying to dynamically change an element's onClick event and I have something like the following:
for (var i = 1; i < 5; i++)
{
getElementById('element' + i).onclick = function() { existingFunction(i); return false; };
}
Everything seems to work fine apart from the fact that the argument passed to 'existingFunction()' is the final value of i=4 each time it is called. Is there a way to bind a function to onclick that uses the value of i at the time of binding as opposed to what it seems to be doing at the moment and referencing the original i in the for-loop.
Also is is there a way of performing the same bind without having to create anonymous functions each time? so that I can directly reference 'existingFunction' in each onclick for performance reasons?
Cheers guys,
Yong
Change
for (var i = 1; i < 5; i++)
{
getElementById('element' + i).onclick = function() { existingFunction(i); return false; };
}
to
for (var i = 1; i < 5; i++)
{
getElementById('element' + i).onclick = createOneHandler(i);
}
function createOneHandler(number){
return function() {
existingFunction(number);
}
}
and it should work fine.
Working Demo
A good explanation is given here
JavaScript, time to grok closures
for the i being always 4, you have a scoping problem, I advise to read this. Scoping is are really important concept, so you have better to make sure to understand what's is going on.
a better code would be
for (var i = 1; i < 5; i++)
{
getElementById('element' + i).onclick = existingFunction;
}
the onclick would pass an event has argument so you can know what element have been clicked
i.e.
function existingFunction(event){
// DO something here
}
you can read more about events there. IE does have the exact same event model as other browser so you would have to handle it.
Last bit, I advise you to use a JS framework(Jquery,ExtJS,DOJO,Prototype...) because it would simplify your task
the code you posted should work the way you intended, your problem with i=4 is elsewhere. edit: this is wrong, rageZ is right about the scoping problem.
re the other question: all you can do is offload the verbosity with
var f = function (i) { return function () { existingFunction(i); return false; } }
for (...) { document.getElementById(...).onclick = f(i); }
BTW, you should use something like jQuery for DOM manipulation (concise syntax), and perhaps Zeta (http://codex.sigpipe.cz/zeta/) for the function composition
var f = compose(false_, existingFunction);
for (...) { $(...).click(f(i));
Hooray! It's loop closures again! See 422784, 643542, 1552941 et al for some more discussion.
is there a way of performing the same bind without having to create anonymous functions each time?
Yes, in ECMAScript Fifth Edition you get function.bind:
for (var i = 1; i < 5; i++)
document.getElementById('element'+i).onclick= existingFunction.bind(window, i);
In the meantime since browsers don't yet generally support it you can monkey-patch an alternative implementation of bind (see the bottom of this comment for one such) built out of anonymous functions as a fallback.
Alternatively, assign the same event handler function to every element and just have it look at this.id to see which element number it is.