Combining js script functions - javascript

How can I combine this to reduce the repetition. What is the best way so I don't have duplicate click functions and do you have any suggestions to combine the lightning functions even though the parameters are different? Thanks
$(document).ready(function(){
var headclix = 0, eyeclix = 0, noseclix = 0, mouthclix = 0;
lightning_one();
lightning_two();
lightning_three();
$("#head").click(function(){
if(headclix < 9){
$("#head").animate({left:"-=367px"}, 500);
headclix += 1;
} else{
$("#head").animate({left:"0px"}, 500);
headclix = 0;
}
})
$("#eyes").click(function(){
if(eyeclix < 9){
$("#eyes").animate({left:"-=367px"}, 500);
eyeclix += 1;
} else{
$("#eyes").animate({left:"0px"}, 500);
eyeclix = 0;
}
})
$("#nose").click(function(){
if(noseclix < 9){
$("#nose").animate({left:"-=367px"}, 500);
noseclix += 1;
} else{
$("#nose").animate({left:"0px"}, 500);
noseclix = 0;
}
})
$("#mouth").click(function(){
if(mouthclix < 9){
$("#mouth").animate({left:"-=367px"}, 500);
mouthclix += 1;
} else{
$("#mouth").animate({left:"0px"}, 500);
mouthclix = 0;
}
})
});//end doc.onready function
function lightning_one(){
$("#container #lightning1").fadeIn(250).fadeOut(250);
setTimeout("lightning_one()", 4000);
}
function lightning_two(){
$("#container #lightning2").fadeIn("fast").fadeOut("fast");
setTimeout("lightning_two()", 5000);
}
function lightning_three(){
$("#container #lightning3").fadeIn("fast").fadeOut("fast");
setTimeout("lightning_three()", 7000);
}

Something like that
var obj = {
headclix: 0,
eyesclix: 0,
noseclix: 0,
mouthclix: 0
}
var types = ['head', 'eyes', 'nose', 'mouth'];
for (var i in types) {
var type = types[i];
$("#" + type).click(function() {
if (obj[type + 'clix'] < 9) {
$("#" + type).animate({ left: "-=367px" }, 500);
obj[type + 'clix'] += 1;
} else {
$("#" + type).animate({ left: "0px" }, 500);
obj[type + 'clix'] = 0;
}
});
}

This is what I had in mind:
$(document).ready(function () {
var head = {clix: 0, id: $("#head")}, eyes = {clix: 0, id: $("#eyes")}, nose = {clix: 0, id: $("#nose")},
mouth = {clix: 0, id: $("#mouth")};
lightning_one();
lightning_two();
lightning_three();
head.click(function () {
body_clix(head.id, head.clix)
})
eyes.click(function () {
body_clix(eyes.id, eyes.clix)
})
nose.click(function () {
body_clix(nose.id, nose.clix)
})
mouth.click(function () {
body_clix(mouth.id, mouth.clix)
})
});//end doc.onready function
function body_clix(b_part_id, clix) {
if (clix < 9) {
b_part_id.animate({left: "-=367px"}, 500);
clix += 1;
} else {
b_part_id.animate({left: "0px"}, 500);
mouthclix = 0;
}
}
function lightning_one() {
$("#container").find("#lightning1").fadeIn(250).fadeOut(250);
setTimeout("lightning_one()", 4000);
}
function lightning_two() {
$("#container").find("#lightning2").fadeIn("fast").fadeOut("fast");
setTimeout("lightning_two()", 5000);
}
function lightning_three() {
$("#container").find("#lightning3").fadeIn("fast").fadeOut("fast");
setTimeout("lightning_three()", 7000);
}

You can try this:
$(document).ready(function() {
var clickCounter = [];
var types = ['head', 'eyes', 'nose', 'mouth'];
//lightning_one();
//lightning_two();
//lightning_three();
types.forEach(function(type) {
clickCounter[type] = 0;
$("#" + type).click(function() {
processClick(this, type);
});
});
function processClick(element, type) {
if (element) {
if (clickCounter[type] < 9) {
$(element).animate({left: "-=367px"}, 500);
clickCounter[type] += 1;
} else {
$(element).animate({left: "0px"}, 500);
clickCounter[type] = 0;
}
console.log(type + ': ' + clickCounter[type]);
}
}
function lightning_one() {
$("#container #lightning1").fadeIn(250).fadeOut(250);
setTimeout("lightning_one()", 4000);
}
function lightning_two() {
$("#container #lightning2").fadeIn("fast").fadeOut("fast");
setTimeout("lightning_two()", 5000);
}
function lightning_three() {
$("#container #lightning3").fadeIn("fast").fadeOut("fast");
setTimeout("lightning_three()", 7000);
}
});
div.holder > div{
display:inline-block;
border: 1px solid black;
margin:10px;
padding:5px;
float:left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Click on one of these: </p>
<div class='holder'>
<div id="head">Head</div>
<div id="eyes">Eyes</div>
<div id="nose">Nose</div>
<div id="mouth">Mouth</div>
</div>

Related

recursive function bug skips the first element in array on the second run

i have this function that prints every letter from array.
here is a link to jsFiddle:
https://jsfiddle.net/yaroslav_cherednikov/ypbuhmqv/71/
it works well on the first run but then skips the first element in array on the second run
var a = ["Saab", "Volvo", "BMW", "renault"];
var d = document.getElementById('out');
var c = document.getElementById("cursor");
window.count = 0;
window.word_count = 0;
setTimeout(function () {
c.style.visibility = 'visible';
function aLoop() {
setTimeout(function () {
if(window.count < a.length){
return lettersPrint(a[window.count]);
}
}, 50);
}
function lettersPrint(word) {
if (window.word_count < word.length) {
setTimeout(function () {
d.innerHTML += word[window.word_count];
window.word_count++;
return lettersPrint(word);
}, 100);
}
else if( window.count < (a.length - 1) ){
setTimeout(function () {
d.classList.add("selected");
}, 1000);
setTimeout(function () {
window.count++;
word_count = 0;
d.classList.remove("selected");
d.innerHTML = '';
return aLoop();
}, 2000);
}
else{
window.count = 0;
aLoop();
}
}
aLoop();
}, 1000);
.typer-txt {
font-size: 2vw;
color: #378bd8;
display: inline-block;
}
#cursor {
float: right;
color: #b9b9b9;
animation: pulse 0.5s infinite;
visibility: hidden;
}
#out {
display: inline;
}
.selected {
background-color: #378bd8;
color: white;
}
#-webkit-keyframes h1-slide-up {
0% {top:100px; opacity: 0;}
100% {top:0px; opacity: 1;}
}
#keyframes pulse {
0% {
opacity: 0
}
100% {
opacity: 1;
}
}
<div class="typer-txt remove" id="typer-txt"><span id="cursor">|</span><div id="out" class=""></div></div>
this is first time i deal with a recursive function so i might have messed something. any help would be much appreciated.
You are not resetting the word count when you are on the last word. I've updated your code and refactored the highlight and erase portion into its own function
var a = ["Saab", "Volvo", "BMW", "renault"];
var d = document.getElementById('out');
var c = document.getElementById("cursor");
window.count = 0;
window.word_count = 0;
setTimeout(function () {
c.style.visibility = 'visible';
function aLoop() {
setTimeout(function () {
debugger;
if(window.count < a.length){
return lettersPrint(a[window.count]);
}
}, 50);
}
function highlightAndErase(winCount) {
setTimeout(function () {
d.classList.add("selected");
}, 1000);
setTimeout(function () {
window.count = winCount
word_count = 0;
d.classList.remove("selected");
d.innerHTML = '';
return aLoop();
}, 2000);
}
function lettersPrint(word) {
// previously was being missed after the last word due to the word_count not being reset
if (window.word_count < word.length) {
setTimeout(function () {
d.innerHTML += word[window.word_count];
window.word_count++;
return lettersPrint(word);
}, 100);
}
else if( window.count < (a.length - 1) ){
highlightAndErase(++window.count)
}
else{
// previously was not resetting the word_count var
highlightAndErase(0)
}
}
aLoop();
}, 1000);
Two problems:
You need a window.word_count = 0 next to window.count = 0 at line 38.
I believe word_count at line 30 needs to be window.word_count.
This will fix the skipped array item, but will introduce a new problem, which I'm sure you'll notice. I'll leave solving that item up to you.
You should have to reset some things in the else clause:
var a = ["Saab", "Volvo", "BMW", "renault"];
var d = document.getElementById('out');
var c = document.getElementById("cursor");
window.count = 0;
window.word_count = 0;
setTimeout(function () {
c.style.visibility = 'visible';
function aLoop() {
setTimeout(function () {
if(window.count < a.length){
return lettersPrint(a[window.count]);
}
}, 50);
}
function lettersPrint(word) {
if (window.word_count < word.length) {
setTimeout(function () {
d.innerHTML += word[window.word_count];
window.word_count++;
return lettersPrint(word);
}, 100);
}
else if( window.count < (a.length - 1) ){
setTimeout(function () {
d.classList.add("selected");
}, 1000);
setTimeout(function () {
window.count++;
word_count = 0;
d.classList.remove("selected");
d.innerHTML = '';
return aLoop();
}, 2000);
}
else{
setTimeout(function () {
d.classList.add("selected");
}, 1000);
setTimeout(function(){
window.count = 0;
window.word_count = 0;
d.classList.remove("selected");
d.innerHTML = '';
aLoop();
}, 2000)
}
}
aLoop();
}, 1000);
Try to separate things in functions to understand better what you are doing! Sometimes recursive things are kinda messy if you don't separate and name things

code does not work outside jsfiddle

There is a jsfiddle code that I'd like to use on my page.
I copied css and put it into <style> tag on my page. Then I separate part that starts with
$(function(){
$('.anyClass').liEqualizer({
and put it into custom.js. And the first part that starts with (function ($) { I put into audio_frequency.js. I added its imports to head tag. The page looks like this
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<style>
.sampleWrap {
margin-left: 6%;
margin-top: 5%;
}
.eqCol {
width: 80px;
margin: 0 0 0 2px;
float: left;
}
.eqItem {
height: 20px;
width: 100%;
background: transparent;
margin: 1px 0 0 0;
opacity: 0;
box-shadow: 15px 20px 0px rgba(0,0,0,0.1);
}
.eqCol .eqItem:last-child {
opacity:1 !important
}
</style>
<script src="jquery-3.2.1.slim.min.js"></script>
<script src="audio_frequency.js"></script>
<script src="custom.js"></script>
</head>
<body>
<div class="sampleWrap d-flex">
<div class="anyClass"></div>
<div style="clear:both; padding:15px 0">
<button class="start">start</button>
<button class="stop">stop</button>
</div>
</div>
</div>
</body>
</html>
custom.js looks like this
$(document).ready(function() {
$('.anyClass').liEqualizer({
row:7,
col:20,
speed:20,
freq:400,
on:true
});
$('.start').click(function(){
$('.anyClass').liEqualizer('start');
return false;
})
$('.stop').click(function(){
$('.anyClass').liEqualizer('stop');
return false;
})
});
and audio_frequency looks like this
(function ($) {
var methods = {
init: function (options) {
var p = {
row: 7,
col: 6,
speed: 20,
freq: 400,
on: true
};
if (options) {
$.extend(p, options);
}
var eqWrap = $(this).addClass('eqWrap');
for (c = 0; c < p.col; c++) {
var eqColEl = $('<div>').addClass('eqCol').appendTo(eqWrap);
for(r = 0; r < p.row; r++){
$('<div>').addClass('eqItem').appendTo(eqColEl);
}
}
var
eqCol = $('.eqCol', eqWrap),
eqItem = $('.eqItem', eqWrap),
randomNumber = function (m, n){
m = parseInt(m);
n = parseInt(n);
return Math.floor(Math.random() * (n - m + 1)) + m;
},
eqUp = function(colEl, val) {
var
speed = p.speed,
v = p.row - val,
i = p.row,
j = 0,
flag2 = true,
eachItemUp = function(){
$('.eqItem', colEl).eq(i - 1).nextAll().stop().css({ opacity:'1' });
if ($('.eqItem', colEl).eq(i - 1).css('opacity') == 1) { flag2 = false }
else { flag2 = true }
$('.eqItem', colEl).eq(i - 1).stop(true).animate({ opacity:'1' }, p.speed, function() {
if ($('.eqItem', colEl).index(this) == v) {
if(flag2) {
eqDown(colEl,val);
}
} else {
i--;
j++;
if(i>v){
eachItemUp()
}
}
})
}
eachItemUp()
},
eqDown = function(colEl,val){
var
v = p.row - val,
i = (p.row-val),
j = 0,
speed = p.speed * 2,
eachItemDown = function(){
if (i == (p.row - val)) {
$('.eqItem', colEl).eq(i).animate({ opacity:'0' }, speed * 10)
setTimeout(function() {
i++;
j++;
if(i < p.row){
eachItemDown();
}
}, speed)
} else {
$('.eqItem', colEl).eq(i).animate({ opacity:'0' }, speed, function(){
i++;
j++;
if(i < p.row){
eachItemDown();
}
})
}
}
eachItemDown();
},
eqInterval = function(){
eqCol.each(function(){
eqUp($(this), randomNumber(0, p.row))
})
}
eqInterval()
if (p.on) {
var eqIntervalId = setInterval(eqInterval, p.freq)
$(this).data({
'eqIntId': eqIntervalId,
'eqInt': eqInterval,
'freq': p.freq,
'on': p.on
})
} else {
$(this).data({
'eqIntId':eqIntervalId,
'eqInt':eqInterval,
'freq':p.freq,
'on':p.on
})
}
}, start: function () {
if (!$(this).data('on')) {
$(this).data('eqInt')();
var eqIntervalId = setInterval($(this).data('eqInt'), $(this).data('freq'));
$(this).data ({
'eqIntId':eqIntervalId,
'on':true
})
}
},
stop: function () {
if($(this).data('on')) {
clearInterval($(this).data('eqIntId'));
$('.eqItem', $(this)).animate({opacity:0})
$(this).data({
'on':false
})
}
}
};
$.fn.liEqualizer = function (method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' in jQuery.liEqualizer does not exist');
}
};
})(jQuery);
But when I load page I get
TypeError: $(...).eq(...).nextAll(...).stop is not a function[Learn More]
What is the problem? Is it trying to evaluate something before all the components are ready on the page?
This $(...).eq(...).nextAll(...).stop error is because of jquery-3.2.1.slim.min.js this ist not complete.
Note:
In the jquery.slim.js, the following functions of code are removed:
jQuery.fn.extend
jquery.fn.load
jquery.each // Attach a bunch of functions for handling common AJAX events
jQuery.expr.filters.animated
ajax settings like jQuery.ajaxSettings.xhr, jQuery.ajaxPrefilter, jQuery.ajaxSetup, jQuery.ajaxPrefilter, jQuery.ajaxTransport,
jQuery.ajaxSetup
xml parsing like jQuery.parseXML,
animation effects like jQuery.easing, jQuery.Animation, jQuery.speedIn the jquery.slim.js, the following function of code are removed:
Here is the complete code, you need jquery lib <script type="text/javascript" src="//code.jquery.com/jquery-1.10.1.js"></script>
<style>
/*Layout css*/
body {
margin: 0;
padding: 20px 10px;
text-align: center
}
.sampleWrap {
height: 290px
}
/*plugin css*/
.eqWrap {
margin: -1px 0 0 -2px;
overflow: hidden;
display: inline-block; //display:inline; //zoom:1;}
.eqCol {
width: 37px;
margin: 0 0 0 2px;
float: left;
}
.eqItem {
height: 10px;
width: 100%;
background: #e7aa3b;
margin: 1px 0 0 0;
opacity: 0
}
.eqCol .eqItem:last-child {
opacity: 1 !important
}
</style>
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.1.js"></script>
<div class="sampleWrap">
<div class="anyClass"></div>
<div style="clear:both; padding:15px 0">
<button class="start">start</button>
<button class="stop">stop</button>
</div>
<div class="anyClass2"></div>
<div style="clear:both; padding:15px 0">
<button class="start2">start</button>
<button class="stop2">stop</button>
</div>
</div>
<script>
/*код плагина*/
(function($) {
var methods = {
init: function(options) {
var p = {
row: 7, //кол-во столбцов
col: 6, //кол-во колонок
speed: 20, //скорость подсветки кубиков
freq: 400, //частота сигнала
on: true //включено по умолчанию (true,false)
};
if (options) {
$.extend(p, options);
}
var eqWrap = $(this).addClass('eqWrap');
for (c = 0; c < p.col; c++) {
var eqColEl = $('<div>').addClass('eqCol').appendTo(eqWrap);
for (r = 0; r < p.row; r++) {
$('<div>').addClass('eqItem').appendTo(eqColEl);
}
}
var
eqCol = $('.eqCol', eqWrap),
eqItem = $('.eqItem', eqWrap),
randomNumber = function(m, n) {
m = parseInt(m);
n = parseInt(n);
return Math.floor(Math.random() * (n - m + 1)) + m;
},
eqUp = function(colEl, val) {
var
speed = p.speed,
v = p.row - val,
i = p.row,
j = 0,
flag2 = true,
eachItemUp = function() {
$('.eqItem', colEl).eq(i - 1).nextAll().stop().css({
opacity: '1'
});
if ($('.eqItem', colEl).eq(i - 1).css('opacity') == 1) {
flag2 = false
} else {
flag2 = true
}
$('.eqItem', colEl).eq(i - 1).stop(true).animate({
opacity: '1'
}, p.speed, function() {
if ($('.eqItem', colEl).index(this) == v) {
if (flag2) {
eqDown(colEl, val);
}
} else {
i--;
j++;
if (i > v) {
eachItemUp()
}
}
})
}
eachItemUp()
},
eqDown = function(colEl, val) {
var
v = p.row - val,
i = (p.row - val),
j = 0,
speed = p.speed * 2,
eachItemDown = function() {
if (i == (p.row - val)) {
$('.eqItem', colEl).eq(i).animate({
opacity: '0'
}, speed * 10)
setTimeout(function() {
i++;
j++;
if (i < p.row) {
eachItemDown();
}
}, speed)
} else {
$('.eqItem', colEl).eq(i).animate({
opacity: '0'
}, speed, function() {
i++;
j++;
if (i < p.row) {
eachItemDown();
}
})
}
}
eachItemDown();
},
eqInterval = function() {
eqCol.each(function() {
eqUp($(this), randomNumber(0, p.row))
})
}
eqInterval()
if (p.on) {
var eqIntervalId = setInterval(eqInterval, p.freq)
$(this).data({
'eqIntId': eqIntervalId,
'eqInt': eqInterval,
'freq': p.freq,
'on': p.on
})
} else {
$(this).data({
'eqIntId': eqIntervalId,
'eqInt': eqInterval,
'freq': p.freq,
'on': p.on
})
}
},
start: function() {
if (!$(this).data('on')) {
$(this).data('eqInt')();
var eqIntervalId = setInterval($(this).data('eqInt'), $(this).data('freq'));
$(this).data({
'eqIntId': eqIntervalId,
'on': true
})
}
},
stop: function() {
if ($(this).data('on')) {
clearInterval($(this).data('eqIntId'));
$('.eqItem', $(this)).animate({
opacity: 0
})
$(this).data({
'on': false
})
}
}
};
$.fn.liEqualizer = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Метод ' + method + ' в jQuery.liEqualizer не существует');
}
};
})(jQuery);
/*инициализация плагина*/
$(function() {
$('.anyClass').liEqualizer({
row: 7, //кол-во столбцов
col: 6, //кол-во колонок
speed: 20, //скорость подсветки кубиков
freq: 400, //частота сигнала
on: true //включено по умолчанию (true,false)
});
$('.start').click(function() {
$('.anyClass').liEqualizer('start');
return false;
})
$('.stop').click(function() {
$('.anyClass').liEqualizer('stop');
return false;
})
$('.anyClass2').liEqualizer({
row: 7, //кол-во столбцов
col: 6, //кол-во колонок
speed: 20, //скорость подсветки кубиков
freq: 400, //частота сигнала
on: false //включено по умолчанию (true,false)
});
$('.start2').click(function() {
$('.anyClass2').liEqualizer('start');
return false;
})
$('.stop2').click(function() {
$('.anyClass2').liEqualizer('stop');
return false;
})
});
</script>

How to run a function in jQuery for 15 times

I have a jQuery function
setInterval(function () {
secondPlay()
}, 1000);
setInterval(function () {
secondPlay1()
}, 1000);
function secondPlay() {
$("body").removeClass("play");
var aa = $("ul.secondPlay li.active");
var ii = $('ul.secondPlay li:last-child').val();
if (aa.html() == undefined) {
aa = $("ul.secondPlay li").eq(0);
aa.addClass("before")
.removeClass("active")
.next("li")
.addClass("active")
.closest("body")
.addClass("play");
}
if (aa.is(":last-child")) {
$("ul.secondPlay li").removeClass("before");
aa.addClass("before").removeClass("active");
aa = $("ul.secondPlay li").eq(0);
aa.addClass("active")
.closest("body")
.addClass("play");
}
else {
$("ul.secondPlay li").removeClass("before");
aa.addClass("before")
.removeClass("active")
.next("li")
.addClass("active")
.closest("body")
.addClass("play");
}
}
I want to run this function for 15 times. How can I run it ?
Declare a variable as a counter. Increment that variable eachtime you calling the function. If the variable reaches 15, the stop the setInterval() by using clearInterval() function
var counter = 1;
var interval = setInterval(function () {
if (counter == 15) {
clearInterval(interval);
}
secondPlay()
counter++;
}, 1000);
You can use following code as reference.
(function(){
var count = 0;
var interval = setInterval(function(){
if(count>15){
window.crearInterval(interval);
}
else{
document.getElementById("lblCount").innerHTML = count;
count++;
}
},1000);
})()
<p id="lblCount"></p>
Try This
var timePlyed = 0;
function secondPlay() {
timePlyed++;
console.log(timePlyed);
if (timePlyed != 15) {
secondPlay();
}
}
secondPlay();
Enclose them in a for loop?
for (i = 0; i < 15; i++) {
...
}
you can use timeout function:
function secondPlay(i){
console.log(i);
}
function test(){
for(var i = 0; i < 15; i++){
setTimeout(function(){
secondPlay(i);
}, i * 1000);
}
}
call test() to execute the function.
Instead of setInterval, you could use a timeout:
var i = 0;
function secondPlay1() {
// do function
setTimeout(function() {
if (i < 15) {
i++;
secondPlay1();
}
}, 1000);
}

Updating interval dynamically - jQuery or Javascript

I have two "stopwatches" in my code (and I may be adding more). This is the code I currently use below - and it works fine. But I'd really like to put the bulk of that code into a function so I'm not repeating the same code over and over.
When I tried doing it though, I could get it working - I think it was because I was passing stopwatchTimerId and stopwatch2TimerId into the function and it may have been passing by reference?
How can I reduce the amount of code repetition here?
var stopwatchTimerId = 0;
var stopwatch2TimerId = 0;
$('#stopwatch').click(function () {
if ($(this).hasClass('active')) {
$(this).removeClass('active');
clearInterval(stopwatchTimerId);
}
else {
$(this).addClass('active');
stopwatchTimerId = setInterval(function () {
var currentValue = parseInt($('#stopwatch-seconds').val()) || 0;
$('#stopwatch-seconds').val(currentValue + 1).change();
}, 1000);
}
});
$('#stopwatch2').click(function () {
if ($(this).hasClass('active')) {
$(this).removeClass('active');
clearInterval(stopwatch2TimerId);
}
else {
$(this).addClass('active');
stopwatch2TimerId = setInterval(function () {
var currentValue = parseInt($('#stopwatch2-seconds').val()) || 0;
$('#stopwatch2-seconds').val(currentValue + 1).change();
}, 1000);
}
});
As you can see, it's basically the same code in each except for stopwatchTimerId and $('#stopwatch-seconds') (and the same vars with 2 on it for the other one).
This won't pollute global scope and also you don't need to do any if-else statements. Just add data-selector to your new elements :)
<input id="stopwatch" type="text" data-selector="#stopwatch-seconds"/>
<input id="stopwatch2" type"text" data-selector="#stopwatch2-seconds"/>
$('#stopwatch stopwatch2').click(function () {
var $element = $(this),
interval = $element.data('interval');
selector = $element.data('selector');;
if ($element.hasClass('active')) {
$element.removeClass('active');
if (interval) {
clearInterval(interval);
}
}
else {
$element.addClass('active');
$element.data('interval', setInterval(function () {
var currentValue = parseInt($(selector).val()) || 0;
$(selector).val(currentValue + 1).change();
}, 1000));
}
});
function stopwatch(id){
$('#' + id).click(function () {
if ($(this).hasClass('active')) {
$(this).removeClass('active');
clearInterval(window[id]);
}
else {
$(this).addClass('active');
window[id] = setInterval(function () {
var currentValue = parseInt($('#' + id + '-seconds').val()) || 0;
$('#' + id + '-seconds').val(currentValue + 1).change();
}, 1000);
}
});
}
$(function(){
stopwatch("stopwatch");
stopwatch("stopwatch2");
});
You could do something like this (code is not very nice, you can improve it):
var stopwatchTimerId;
$('#stopwatch').click(function () {
doStopWatch(1);
});
$('#stopwatch2').click(function () {
doStopWatch(2);
});
var doStopWatch = function(option){
var stopWatch = option===1?$('#stopwatch'):$('#stopwatch2');
if (stopWatch.hasClass('active')) {
stopWatch.removeClass('active');
clearInterval(stopwatchTimerId);
}
else {
stopWatch.addClass('active');
stopwatchTimerId = setInterval(function () {
var currentValue = option===1?(parseInt($('#stopwatch-seconds').val()) || 0):(parseInt($('#stopwatch2-seconds').val()) || 0);
if(option===1)
$('#stopwatch-seconds').val(currentValue + 1).change();
else
$('#stopwatch2-seconds').val(currentValue + 1).change();
}, 1000);
}
}
Try
var arr = $.map($("div[id^=stopwatch]"), function(el, index) {
el.onclick = watch;
return 0
});
function watch(e) {
var id = this.id;
var n = Number(id.split(/-/)[1]);
if ($(this).hasClass("active")) {
$(this).removeClass("active");
clearInterval(arr[n]);
} else {
$(this).addClass("active");
arr[n] = setInterval(function() {
var currentValue = parseInt($("#" + id + "-seconds").val()) || 0;
$("#" + id + "-seconds").val(currentValue + 1).change();
}, 1000);
}
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div id="stopwatch-0">stopwatch1</div>
<input type="text" id="stopwatch-0-seconds" />
<div id="stopwatch-1">stopwatch2</div>
<input type="text" id="stopwatch-1-seconds" />

Undefined index after running function a few times

So I was trying to create my own Blackjack in javascript for learning purposes and even though the code is overall working, I came across a weird bug.
After some clicks on the Deal html button, which calls the function deal(), I will get either a playerHand[i] undefined or dealerHand[i] undefined error on line 114 or 118, respectively, of the code posted below.
I noticed this also happened if I clicked the button very fast for whatever reason.
I suspected it had something to do with memory optimization so I used the delete command to reset those arrays between game turns, but the error persists.
So, why do my arrays break after some use?
Thanks.
JS:
var deck = [];
var dealerHand = [];
var playerHand = [];
var dscore = 0;
var pscore = 0;
var turn = 0;
function Card(suit, src) {
this.src = src;
this.suit = getSuit(suit);
this.value = getValue(src);
};
function getSuit(suit) {
if (suit == 1) return "Clubs";
if (suit == 2) return "Diamonds";
if (suit == 3) return "Hearts";
if (suit == 4) return "Spades";
};
function getValue(src) {
if (src == 1) return 11;
if (src < 10) return src;
else return 10;
};
function createDeck() {
for (i=1; i<=4; i++) {
for(j=1; j<=13; j++) {
var card = new Card(i, j);
deck.push(card);
};
};
};
function getCard() {
var rand = Math.floor(Math.random()*deck.length);
deck.splice(rand,1);
return deck[rand];
};
function deal() {
if(turn == 0) {
dealerHand.push(getCard());
playerHand.push(getCard());
};
dealerHand.push(getCard());
playerHand.push(getCard());
};
function stand() {
dealerHand.push(getCard());
};
function clearBoard () {
$('#player').html("");
$('#dealer').html("");
};
function resetDeck () {
delete deck;
deck = [];
};
function resetHands () {
delete dealerHand;
delete playerHand;
dealerHand = [];
playerHand = [];
};
function resetScore () {
pscore = 0;
dscore = 0;
};
function isAce (arr) {
for(i=0; i<arr.length; i++) {
if (arr[i].src == 1) return true;
else return false;
};
}
function updateScore() {
resetScore();
if (playerHand.length > 0 && dealerHand.length > 0) {
for(i=0; i<playerHand.length; i++) {
pscore += playerHand[i].value;
};
for(i=0; i<dealerHand.length; i++) {
dscore += dealerHand[i].value;
};
//Regra do Às
if(pscore > 21 && isAce(playerHand)) {
pscore -= 10;
};
if(dscore > 21 && isAce(dealerHand)) {
dscore -= 10;
};
} else {
pscore = 0;
dscore = 0;
};
};
function showScore () {
$('#pscore').html("<p>Player Score: " + pscore + "</p>");
$('#dscore').html("<p>Dealer Score: " + dscore + "</p>");
};
function showCards () {
for(i=0; i<playerHand.length; i++) {
var div = $("<div>");
var img = $("<img>");
img.attr('src', 'img/cards/' + playerHand[i].suit + '/' + playerHand[i].src + '.png');
div.append(img);
$('#player').append(div);
};
for(i=0; i<dealerHand.length; i++) {
var div = $("<div>");
var img = $("<img>");
img.attr('src', 'img/cards/' + dealerHand[i].suit + '/' + dealerHand[i].src + '.png');
div.append(img);
$('#dealer').append(div);
};
};
function cleanUp () {
if (pscore == 21) {
alert("Blackjack!");
newGame();
};
if (pscore > 21) {
alert("Bust!");
newGame();
};
if (dscore == 21) {
alert("You lost!");
newGame();
};
if (dscore > 21) {
alert("You won!");
newGame();
};
};
function newGame () {
turn = 0;
clearBoard();
resetHands();
resetScore();
showScore();
resetDeck();
createDeck();
};
function gameTurn () {
clearBoard();
updateScore();
showCards();
showScore();
cleanUp();
turn++;
};
$(document).ready(function() {
newGame();
$('#deal').on('click', function(){
deal();
gameTurn();
});
$('#stand').on('click', function(){
stand();
gameTurn();
});
});
CSS:
body {
background: url(../img/greenbg.png);
}
.holder {
width:800px;
margin:auto;
}
.clearfix {
clear:both;
}
#pscore, #dscore {
color: white;
margin: 10px;
display: block;
font-size: 1.2rem;
text-shadow: 0 0 5px #000;
}
.container {
width: 600px;
height: 300px;
margin: 10px;
}
div img {
float: left;
margin: 10px;
}
div button {
margin: 10px;
}
HTML:
<html>
<head>
<div class="holder clearfix">
<div id="dscore"><p>Dealer Score: 0</p>
</div>
<div id="dealer" class="container">
</div>
<div id="pscore"><p>Player Score: 0</p>
</div>
<div id="player" class="container">
</div>
<div class="">
<button id="deal">Deal</button>
<button id="stand">Stand</button>
</div>
</div>
</body>
</html>
You have a problem in this function, which may be to blame:
function getCard() {
var rand = Math.floor(Math.random()*deck.length);
deck.splice(rand,1);
return deck[rand];
};
As written, it's removing a card, and then returning the card that now has that position in the deck. If rand was the last element in the array then there is no longer a card in that position, so it'll return undefined.
You should be returning the value of the removed card itself, part of the result of the splice call:
function getCard() {
var rand = Math.floor(Math.random() * deck.length);
var pick = deck.splice(rand, 1);
return pick[0];
};
p.s. it's worth learning modern ES5 utility functions for arrays. For example, your isAce function could be rewritten thus, avoiding the bug where you always return after testing the first element:
function isAce(arr) {
return arr.some(function(n) {
return n === 1;
});
};
or, more cleanly:
function isAce(card) {
return card === 1; // test a single card
};
function holdsAce(hand) {
return hand.some(isAce); // test an array (or hand) of cards
};

Categories