jQuery: After mouseOver on link#1, 2 and 3 - activate show content - javascript

I'm working on a easter egg, where you have to activate some links in the correct order, before the secret is revealed.
I can't get this script to work. I guess I've wrote something wrong, but can't see what it is...
<script>
$(document).ready(function() {
$('#show').hide();
var StepOfThree = 0;
alert(StepOfThree);
$('#linkone').mouseover(function() {
StepOfThree = 1;
alert(StepOfThree);
});
$('#linktwo').mouseover(function() {
if (StepOfThree1 === 1) {
StepOfThree = 2;
alert(StepOfThree);
} else {
StepOfThree = 0;
alert(StepOfThree);
}
});
$('#linkthree').mouseover(function() {
if (StepOfThree1 === 2) {
$('#show').show();
alert(StepOfThree);
} else {
StepOfThree = 0;
alert(StepOfThree);
}
});
});
</script>
Link #1
Link #2
Link #3
<div id="show">This is hidden content</div>
The mouseOver on the #linkTwo and #linkThree doesn't even give me an Alert.. What have I done wrong?

Why are you using jQuery for Javascript primitives? This is horribly wrong! This seems like a case of jQuery-itis (sorry for the Google cache link) - use the Javascript equality operator, and don't wrap your StepOfThree for a simple numeric comparison:
if (StepOfThree === 1)
{
// do stuff
}
My guess is you're learning Javascript at the same time as jQuery, right?
Update:
Okay, here's why your second and third handlers don't work as you expect: when you create a callback like the way you are, you're creating a closure. In effect, this "seals" the value of StepOfThree into the mouseover handlers so they never see the updated value.
Try doing it like this instead:
http://jsbin.com/ovocu/6
This way, you're closing on an object (a "reference" or "pointer" if you're familiar with C/C++/Java) rather than the primitive value of the number itself.
Some good reading on Javascript closures.
Update 2: for the simplest working example, here's what Daniel had to offer (from the comments below): http://jsbin.com/iluse3
No need to pass around an object. Sorry for any confusion this caused!

Why not use the built in operators to check equivalence? The is method is meant to test selectors on jQuery objects, not to test equivalence on regular objects or variables.
Instead of:
if ($(StepOfThree).is(1))
Use:
if (StepOfThree === 1) {

You have redeclared your variable, overwriting its scope.
...
var StepOfThree = 0;
$('#linkone').mouseover(function() {
var StepOfThree = 1;
});
...
There are two variables there, both named 'StepOfThree'. To fix, take away the 'var' for all but the first declaration.

You need to remove your vars
<script>
$(document).ready(function() {
$('#show').hide();
var StepOfThree = 0;
$('#linkone').mouseover(function() {
StepOfThree = 1;
});
$('#linktwo').mouseover(function() {
if (StepOfThree == 1) {
StepOfThree = 2;
} else {
StepOfThree = 0;
}
});
$('#linkthree').mouseover(function() {
if (StepOfThree == 2 ) {
$('#show').show();
} else {
StepOfThree = 0;
}
});
});
</script>
You are including jquery right?

What's happening is that your StepOfThree variable is not global - it is inside your $(document).ready function. Declare it outside:
script open tag
var StepOfThree=0;
$(document).ready stuff

Related

How can I write this function without using a modulo?

I'm wondering if there is another way to write this function without using a modulo. I realized that I have another piece of code that requires me to click the #mail-wrap button and doing so messes up the number of clicks which affects this function.
It's just a simple switch. I'm not too good with conditionals.
$('#mail-wrap').click(function (e) {
e.preventDefault();
var c = 0;
if (c++ % 2 == 0) {
$('#contact-button').addClass('project-button').text('Projects');
} else {
$('#contact-button').removeClass('project-button').text('Get in touch');
}
});
Edit: Changed the question a bit. Sorry, the last one was too broad.
As Boldewyn mentioned, most likely your problem is that you are defining a global variable c. But if you would like to avoid this variable completely you could check for the CSS-class of contact-button via the jQuery hasClass function, i.e.
$('#mail-wrap').click(function (e) {
...
var contactButton = $('#contact-button');
if (!contactButton.hasClass('project-button')) {
$('#contact-button').addClass('project-button').css('width', '71px').text('Projects');
...
} else {
$('#contact-button').removeClass('project-button').css('width', '96px').text('Get in touch');
...
}
});
The code is interfering with other code, because you have implicitly generated a global variable c. Possible fix: Use an IIFE:
(function() {
var c = 0;
/* rest of your code above ... */
})();

What are the pitfalls of adding properties to a function object within the function?

I found I could use this technique to retain a sort of "state" within an event handler, w/o having to involve outside variables...
I find this technique to be very clever in leveraging the fact that functions are actually objects in and of themselves, but am worried I'm doing something that could have negative implications of some sort...
Example...
var element = document.getElementById('button');
element.onclick = function funcName() {
// attaching properties to the internally named "funcName"
funcName.count = funcName.count || 0;
funcName.count++;
if (self.count === 3) {
// do something every third time
alert("Third time's the charm!");
//reset counter
funcName.count = 0;
}
};
Instead of doing that, you can use a closure:
element.onclick = (function() {
var count = 0;
return function(ev) {
count++;
if (count === 3) {
alert("3");
count = 0;
}
};
})();
That setup involves an anonymous function that the code immediately calls. That function has a local variable, "count", which will be preserved over the succession of event handler calls.
By the way, this:
var something = function dangerous() { ... };
is "dangerous" because some browsers (guess which, though Safari has had issues too) do weird things when you include a name on a function expression like that. Kangax wrote the issue up quite thoroughly.

JSHint error Don't make functions within a loop

I'm running some code through JSHint and I keep getting the following error:
Don't make functions within a loop.
I tried turning off the warning for 'About functions inside loops' off which does nothing to stop the error from being reported. I have decided to refactor the code, using JSHint's suggestions here, http://www.jshint.com/options/ but I'm still getting the error. I was hoping that somebody could help me to refactor this code slightly so it will pass. Here's a copy of the function:
function setSounds(parent) {
var i,
l;
parent.getElements('.sound').each(function (elem) {
var soundEvents = [];
if (elem.get('fk_click_sound')) {
soundEvents.push('click');
}
if (elem.get('fk_mouseover_sound')) {
soundEvents.push('mouseenter');
}
if (soundEvents.length !== 0) {
for (i = 0, l = soundEvents.length; i < l; i += 1) {
elem.addEvent(soundEvents[i], (function () {
return function (e) {
FKSoundAIR(FKSoundStd[this.get('fk_' + e.type + '_sound')]);
};
})(elem), false);
}
}
});
}
I'm using MooTools. The purpose of this function is to pass a parent element and then apply sound event to all of the children with the class 'sound.' I'm using custom HTML attributes, such as 'fk_click_sound' to feed additional information to the function. I picked up this method of assigning a function within a loop from http://blog.jbrantly.com/2010/04/creating-javascript-function-inside.html.
Any suggestions or resources that you can point me to would be great. Thanks!
You can try something like this:
function make_handler(div_id) {
return function () {
alert(div_id);
}
}
for (i ...) {
div_id = divs[i].id;
divs[i].onclick = make_handler(div_id);
}
You could create the function outside, assign it to a var and use it in your call to addEvent.
As it turns out JS Hint had a bug re: the warning for Looping inside of a function, which they fixed here. Now that this is fixed, this issue is resolved.

De-Anonymizing an Anonymous Function in jQuery

This is probably quite a simple problem, but it's causing me to scratch my head, so I'm posting it here.
I have some jQuery in the following form:
if (jQuery('.SearchRegions:checked').length == 0) {
jQuery('.SearchRegions').each(function(){
//code
});
} else {
jQuery('.SearchRegions:checked').each(function(){
//the same code
});
}
Obviously it seems ridiculous to repeat a big block of code inside each of these functions. But when I tried to name and move the function, it all seemed to break down - perhaps because of issues with scope and/or jQuery(this) inside the function no longer referring to the same object?
Can anyone help me by posting a general idea of what my code should look like? (Or any other optimisings or recastings to make it work would be much appreciated!)
You can definitely just define a function and use it by name:
function someHandler(event) {
// code code code
}
jQuery('.SearchRegions').each(someHandler);
Note that when you refer to the function by name, you don't include "()".
Assuming that closures are indeed the problem, you can parameterize your "anonymous" function to pass those values in to it. For example:
function eachRegion(values, $container, foo) {
// common code which uses scope variables `values`, `$container`, and `foo`
}
// elsewhere, in code defining `values`, `$container`, and `foo`...
if (jQuery('.SearchRegions:checked').length == 0) {
jQuery('.SearchRegions').each(function(){
eachRegion(values, $container, foo);
});
} else {
jQuery('.SearchRegions:checked').each(function(){
eachRegion(values, $container, foo);
});
}
You could define your function as a variable and use that in your each method call.
var yourEachFunction = function(){$("ul").append("<li>" + $(this).val() + "</li>");}
if (jQuery('.SearchRegions:checked').length == 0) {
jQuery('.SearchRegions').each(yourEachFunction );
} else {
jQuery('.SearchRegions:checked').each(yourEachFunction );
}
Example of this working on jsfiddle.

Javascript variable scope question

I'm having trouble resolving a scope issue with my javascript.
I have an array, dog[] that is defined from JSON, that I need access to from inside a nested function.
function blah(json) {
for (var u = 0; u < json[0][1][u].length; u ++ ) {
var dog = 'k' + json[0][1][u].doggies;
console.log(dog); // prints array of doggie strings
$('#puppy').click(function(dog) { // dog is passed in the function
console.log(dog); // Syntax error, unrecognized expression: #[object Object]
$('#' + dog).css('display, 'none');
});
}
}
when I dont pass dog into the click function: i get:
$('#puppy').click(function() {
console.log(dog) // (12) main.js:122k4c812e3a7275e10331000000 - this is the last value in the array - from safari console
$('#' dog).css('display', 'none);
}
Does anyone have any suggestions to get the array with every element passed into the click function?
Or am i calling the css method incorrectly to hide those divs?
Problem 1
Closures bind the entire function's scope, and not individual variables or values.
Take this code for example:
function foo() {
var i, func;
for (i = 0; i < 10; ++i) {
if (i == 0) {
func = function () {
alert(i);
}
}
}
func();
}
foo();
You may expect foo to cause 0 to be alerted. However, the value of i has changed since the function assigned to func was created; the call to func alerts "10".
Here is another example illustrating the concept:
function foo() {
var i = 42;
function func() {
alert(i);
}
for (i = 0; i < 10; ++i) {
// do nothing
}
func();
}
foo();
Try to figure out what will be alerted, and run the code as a test.
Problem 2
The second problem is that variables are bound at the function scope (and not the block scope as you expect).
Take this code:
function foo() {
var i;
for (i = 0; i < 10; ++i) {
var j = i;
}
alert(j);
}
foo();
You may expect this code to alert "undefined", throw a run-time error, or even throw a syntax error. However, "10" is alerted. Why? In JavaScript, the above code is translated into effectively:
function foo() {
var i;
var j;
for (i = 0; i < 10; ++i) {
j = i;
}
alert(j);
}
foo();
It should be more clear from this example that "10" is indeed alerted.
Solution
So how do you fix your problem? The simplest way is to change your logic: instead of attaching one event handler per dog, attack one event handler per collection of dogs. For example:
function blah(json) {
$('#puppy').click(function () {
var u, dog;
for (u = 0; u < json[0][1][u].length; u++) {
dog = 'k' + json[0][1][u].doggies;
console.log(dog);
$('#' + dog).css('display', 'none');
}
});
}
If you're interested in the "proper" transformation of your existing code (i.e. having the same behaviours, except with the bug fixed), I can give you an example of that as well. However, the solution I gave above is a much better solution and results in cleaner code.
Important Note:
You forgot to close your quote. This:
$('#' + dog).css('display, 'none');
Should be:
$('#' + dog).css('display', 'none');
An Improved Loop:
There are several problems with your script. I'll concentrate on the overall logical structure of the loop.
Instead of attaching many handlers to .click(), just attach one handler that iterates over you JSON using jQuery's .each(). The first argument of the callback of .each() is the index number and the second argument is the value. You can make use of those 2 by naming the arguments or by using arguments[0] and arguments[1]. I show the former method below:
I've added some more test output for demonstration purposes:
function blah(json) {
$('#puppy').click(function() {
// iterate over each json[0][1]
$.each(json[0][1], function(index, value) {
// Your original 2 lines
console.log(value);
$('#' + value).css('display', 'none');
// This is just test output, so you can see what is going
// on.
$("body").append("Number " + index + " is " + value ".<br/>");
});
});
}
Why not just give the doggies a class .dog and hide them when #puppy is clicked?
$("#puppy").click(function() {
$(".dog").hide();
});
Or since your dog's IDs seem to start with k, you might consider something like this:
$("#puppy").click(function() {
// hide everything with ID beginning with 'k'
$('[id^=k]').hide();
});
You can't pass the dog value into the jquery click event as you have done there. The click function signature is:
$(object).click(function(){
});
You can't pass dog in like this. Even if the function expected a parameter, naming it dog would cause issues. You may need to store the values of dog in a more global scope so that when the click event occurs, you still have access to it.

Categories

Resources