How can I write this function without using a modulo? - javascript

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 ... */
})();

Related

Function call inside a for loop breaks the loop

I have a small fiddle I was experimenting with, and I noticed calling a function inside a for loop condition was stopping the loop. Basically, initially I wanted to do this:
// add event listeners to tabs
for (i=0;i<tabs.length;i++) {
tabs[i].addEventListener('click', function(event) {
var tab = event.target;
selectPage(tab.dataset.tab);
changeTab(tab);
});
if (tabs[i].classList.contains('active')) {
selectPage(tabs[i].dataset.tab);
}
}
But, ended up having to do this to make it work:
// add event listeners to tabs
for (i=0;i<tabs.length;i++) {
tabs[i].addEventListener('click', function(event) {
var tab = event.target;
selectPage(tab.dataset.tab);
changeTab(tab);
});
}
// find active class and set page
for (i=0;i<tabs.length;i++) {
if (tabs[i].classList.contains('active')) {
selectPage(tabs[i].dataset.tab);
}
}
Here is a link to the Fiddle
Thanks for any help in advance, I feel there is something fundamental here I'm not getting. Thanks
Lesson 0: use ESLint or similar tools to check your code for trivial errors before spending sleepless nights here on SO and/or in debugging tools.
Lesson 1: localize your variables.
Your problem is with variable i that's global - hence reused by both your global code and selectPage function. The latter sets its value to tabs.length, ending up the loop prematurely.
Just replace i = 0 with var i = 0 at each for expression.
Try declaring the x variable using let.
// add event listeners to tabs
for (let i=0;i<tabs.length;i++) {
tabs[i].addEventListener('click', function(event) {
var tab = event.target;
selectPage(tab.dataset.tab);
changeTab(tab);
});
if (tabs[i].classList.contains('active')) {
selectPage(tabs[i].dataset.tab);
}
}

Javascript running code once

I only want my JavaScript to run once, but I cannot control how many times the javascript file is executed. Basically I'm writing a tiny JS snippet into a CMS, and the CMS is actually calling it 5-10 times. So solutions like this:
function never_called_again(args) {
// do some stuff
never_called_again = function (new_args) {
// do nothing
}
}
never_called_again();
Don't seem to work because as soon as my snippet is run again from the top the function is re-declared, and 'do some stuff' is re-evaluated. Perhaps I'm just not doing it properly, I'm not great with JS. I'm considering using something like try-catch on a global variable, something like
if (code_happened == undefined) {
\\ run code
code_happened = true;
}
EDIT: There is a consistent state e.g. if I set a variable I can see when my snippet is run again. But having to declare it before I access it, I don't know how to say 'does this variable exist yet'
Try this:
var doneTheStuff;
function whatever() {
if (!doneTheStuff) {
doneTheStuff = true;
// do the stuff
}
}
Redundant variable declarations don't affect the value of the variable. Once one of the functions has set the variable to true, the others won't do anything.
if (typeof code_happened === 'undefined') {
window.code_happened = true;
// Your code here.
}
The typeof check gets you around the fact that the global hasn't been declared. You could also just do if (!window.code_happened) since property access isn't banned for undefined properties.
Use a closure, and set a flag. If the flag is true, just return:
if ( ! window.never_called_again ) {
window.never_called_again = (function () {
var ran = false;
return function (args) {
if ( ran ) return;
ran = true;
// Do stuff
};
}());
}
Here's the fiddle: http://jsfiddle.net/U2NCs/
With jQuery, the function .one() may be useful : http://api.jquery.com/one/
W3School exemple here : http://www.w3schools.com/jquery/event_one.asp
In this way, the code is executed only once.
if(typeof onceRun == "undefined") window.onceRun=(
()=>{
//your codes...
console.log("runing...")
return true
}).call()

Spinlock in Javascript

How can I do a spinlock in javascript?
I'm trying to load a bunch of images and I can only move forward after everything is loaded, so I have a spinlock like
for(...)
image[i].onload = function() { ++imagesloaded; }
while(imagesloaded != totalimages)
{
}
And it crashes my browser. Is there a better way to do it? Or a yield / sleep function I'm missing?
Short answer: don't spinlock.
Longer answer: here's how to do it:
var imagesLoaded = 0;
var totalImages = 42;
function handleImageLoad()
{
imagesLoaded++;
if (imagesLoaded === totalImages)
{
doSomething();
}
}
for (var i=0; i<totalImages; i++)
{
image[i].onload = handleImageLoad;
}
In general, when you want to sleep/wait/spin in JavaScript, instead think about solving the problem in terms of callbacks (and setTimeout/setInterval).
The answers above aren't useful as spinlocks may be required because of limitations/bugs in browsers. For instance safari (hopefully not future versions) requires the use of method window.open when you want to generate a file in javascript. The consequence of this is that you cannot generate the file using any callbacks (because of popup blockers), this in effect forces the use of a dialog window that first calls the file generation function (using callbacks) and then a button that downloads the file. Because spinlocks don't work the code becomes the following:
function process(callback) {
processCallbackData = null; // global var that must be a unique name
callback(function(data) {
processCallbackData = data;
});
}
function fowardButton() {
if(processCallbackData!=null) {
goForwardUsingCallbackIncompatibleCode();
} else {
displayStillLoadingWarning();
}
}
Don't use a loop to check. Check in the event handler function. (So you only do the check when an image has loaded, not continuously and as quickly as possible)

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.

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

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

Categories

Resources