jQuery function running multiple times despite input being disabled? - javascript

I'm having an issue where a function (and subsequent callback functions) continue to run after the first keypress, despite the fact that I'm specifically disabling the input and unbinding keypress. The weird thing is, these additional results only start appearing after a significant amount of time has passed (10+ seconds I would guess). You can see this error in action by going to http://isitsketch.com and mashing on the enter button while entering an address. The relevant code is this:
$('.location-form').keypress(function(event){
if (event.which == 13){
$(this).find('input').attr('disabled','disabled');
$(window).unbind("keypress")
console.log('search entered')
event.preventDefault();
userAddress = $("#input1").val()
address = userAddress.replace(/\s+/g, '+');
getMaps(address)
event.stopPropagation()
return false;
} else {
return true;
}
});
The function that displays the results is this:
function displayResults(sketchLevel, robberies, assaults, burglaries, thefts, weapons, topFiveArr) {
$('#newResults').append('<h4 id="feedback" style="color:white;text-align:center;">' + userAddress + '</h4>')
$("<h2 id='result-level'>Sketch Level: <span class='redText'>"+ sketchLevel +"</span></h2>").appendTo("#newResults")
$('<div id="indicator"><div id="bar"></div><div id="dial"></div></div>').appendTo("#newResults")
$('<a id="tryAgain" class="waves-effect waves-light btn" href="http://isitsketch.com">Enter a New Address</a>').appendTo("#newResults")
$('<p id="explanation">These results are based on NYC\'s OpenData information platform. The crimes have been weighted to reflect their severity. The resulting SketchLevel ranges from 0 (no crimes) to 2000+ (lots of severe crime activity).</p>').appendTo("#newResults")
$('#newResults').append('<h4 id="topFive">Can you beat these scores?:</h4><ul id="topList"></ul>')
for (i = 0; i < 5; i++){
var currentTopArea = topFiveArr[i]['area']
var currentTopRank = topFiveArr[i]['rank']
$('#topList').append('<li style="color:white; font-size:1em;">' + currentTopArea + ': ' +'<span id="rankText">' + currentTopRank + '</span>' +'</li>')
}
var audio = new Audio('sound/gunsound.mp3')
audio.play();
if(sketchLevel <= 400) {
$('#dial').addClass('sketchlevel1')
}
if(sketchLevel > 400 && sketchLevel <= 800) {
$('#dial').addClass('sketchlevel2')
}
if(sketchLevel > 800 && sketchLevel <= 1400){
$('#dial').addClass('sketchlevel3')
}
if(sketchLevel > 1500 && sketchLevel <= 2000) {
$('#dial').addClass('sketchlevel4')
}
if(sketchLevel > 2000) {
$('#dial').addClass('sketchlevel5')
}
}

Since the keypress event handler is bound to .location-form, you need to unbind it from that element, not window. So it should be:
$('.location-form').unbind("keypress");
Also, .off() is preferable to .unbind(), unless you need to be compatible with jQuery versions before 1.7.

I figured out how to stop it, although I still don't know why it was occurring in the first place. The fix was this:
var enabled = true;
displayResults(){
if (enabled) {
...the rest of the code here
enabled = false;
}
}
That said, it would be awesome if somebody else could figure out how/why it was getting around the input-disabled line and then continuing to post every 10 seconds or so. Just so weird.

Related

Function starts being repeated over and over again

I've been developing a simple system that is supposed to change between two different scenes when you press a button.
gameOne();
var game = 1;
function gameOne() {
game = 1;
console.log("Game 1");
$( "body" ).keyup(function( event ) {
if ( event.which == 49 && game == 1) { // Number 1 key
gameTwo();
}
});
}
function gameTwo() {
game = 2;
console.log("Game 2");
$( "body" ).keyup(function( event ) {
if ( event.which == 49 && game == 2) { // Number 1 key
gameOne();
}
});
}
Expected behaviour - I want it to say Game 1, after after pressing the 1 key and then Game 2 after pressing the 1 key again, and then repeat this as I press 1.
Actual behaviour - It does the expected behaviour a few times, and then it starts repeating 1 and 2 over and over again, to the point it lags the browser out.
JSFiddle: https://jsfiddle.net/a0npotm8/10/
I'm really sorry if this is a basic question or anything, I'm still fairly new to Javascript and JQuery and this is really confusing me currently.
All help is appreciated.
Thank you :)
The problem here is that you are rebinding the keyup event recuresively inside the keyup callback, so it ends up by breaking the browser.
What you need to do is to get the keyup binding code out of the two functions:
gameOne();
var game = 1;
$("body").keyup(function(event) {
if (event.which == 49 && game == 1) { // Number 1 key
gameTwo();
} else if (event.which == 49 && game == 2) { // Number 1 key
gameOne();
}
});
function gameOne() {
game = 1;
console.log("Game 1");
}
function gameTwo() {
game = 2;
console.log("Game 2");
}
what about something like:
let game = 1;
document.onkeyup = ev => {
if (ev.which === 49) {
console.log(`Game ${game}`);
game = game === 1 ? 2 : 1;
}
};
will it solve your issue?
You can use a delegate event handler to control actions like this, so you do not have to juggle event bindings around.
var $container = $('#container').focus();
$(document.body)
.on('keyup', '#container.game1', function(e){
if (e.which == 49) {
console.log('Game 1');
$container.removeClass('game1').addClass('game2');
}
})
.on('keyup', '#container.game2', function(e){
if (e.which == 49) {
console.log('Game 2');
$container.removeClass('game2').addClass('game1');
}
});
#container {
min-width: 100vw;
min-height: 100vh;
background-color: rgb(128, 128, 128);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container" class="game1" tabindex="0">
</div>
This logic creates two different delegate event handlers for the body. Both filter out events for the #container child element of the body, but also filter based on an additional class on the container; game1 and game2. Depending on which class the element has, only one of the event handlers will process.
Whenever you call keyup on an element, you attach another event handler. To catch events, you only need to call it once. The callback functions that handle the event will fire every time the event happens.

jQuery keyup working incorrectly

I am trying to make an integer variable increment every time the Enter key is pressed. This variable is then used to output a value from an array.
$(document).keyup(function(e) {
if (e.keyCode == 13) {
console.log(++currSteps);
$('#output').text(outNum[currSteps])
if(outNum[currSteps] % 2 == 0){
$('body').css("background", "#4b84e5")
}
else if(outNum[currSteps] == 1){
$('body').css("background", "#9dd622")
}
else{
$('body').css("background", "#d16323")
}
}
});
The problem occurs when I actually press Enter. On the first press, it works properly, and increments the variable currSteps and displays the next value in the array. However, when I press Enter again, it reverts it back to the original value of currStep, as long as Enter is held down, it will display the original value.
Now, yes, this event is for keyup, therefore it makes sense that things will work oddly on keydown. However... if I open up the developer tools and click around in the console, and then return to my original window, the keyup code works perfectly. Why is this only working after the developer tools have been opened?
I have tried this on jQuery 1.11.1 and 1.10.2.
Edit: currSteps and outNum declarations.
$('#in').click(function(){
$('#output').empty();
$('#steps').empty();
var steps = 0
currSteps = 0
var inNum = parseInt($('#input').val());
var col = '#ffffff'
outNum = []
outNum.push(inNum)
...
I allowed to myself to write a little of your missing variables defition: JSFiddle
$(document).ready(function() {
var currSteps = 0;
var outNum = { 0: 0, 1: 1, 2: 2 };
$(document).keyup(function(e) {
if (e.keyCode == 13) {
console.log(++currSteps);
$('#output').val(outNum[currSteps] + ' ' + currSteps)
if(outNum[currSteps] % 2 == 0){
$('body').css("background", "#4b84e5")
}
else if(outNum[currSteps] == 1){
$('body').css("background", "#9dd622")
}
else{
$('body').css("background", "#d16323")
}
}
});
});
Is you problem still actual with upper demo?

Insert delay between keypress events and set display time

I'm trying to insert delays in a sequence of stimuli that are presented on the keypress event.
The code below shows how I'd tried to insert a 1000ms delay between stimuli (i.e. after the key was pressed, it should take 1000ms before the next stimulus appears). Yet there seems to be no delay. Where am I wrong (is it better to do this with setTimeout)?
And: how could I set the maximum 'display-time' of each stimulus to, say, 2000ms, so that it automatically presents another stimulus even if no key is pressed?
var stim = [
{name:"WORD1", path:".../pic1.jpg"},
{name:"WORD2", path:".../pic2.jpg"},
{name:"WORD3", path:".../pic3.jpg"},
]
$(function(){
$(document).keypress(function(e){
if ($(e.target).is('input, textarea')) {
return;
};
if (e.which === 97 || e.which === 108) {
if(Math.random() < 0.5) {
var new_word = stim[Math.floor((Math.random()*stim.length)+1)].name;
$("#abc").delay(1000).text(new_word);
} else {
var new_img = stim[Math.floor((Math.random()*stim.length)+1)].path;
$("#abc").delay(1000).empty();
var prox_img = $('<img id="abcimg" height="300px" width="300px">');
prox_img.attr('src', new_img);
prox_img.appendTo('#abc');
}
};
});
});
According to the documentation for delay: https://api.jquery.com/delay/
The .delay() method is best for delaying between queued jQuery
effects.
You should be using setTimeout for this since it also allows you to cancel it. That is really important for your need to have a maximum timeout.
So, first I would break your function up. You could have a function that handles the displaying, which will get called when timeout occurs or when a key is pressed.
$(function(){
var to = 0; //this is used to clear your timeout when the user clicks a button
function showNext() {
if(Math.random() < 0.5) {
var new_word = stim[Math.floor((Math.random()*stim.length)+1)].name;
$("#abc").delay(1000).text(new_word);
} else {
var new_img = stim[Math.floor((Math.random()*stim.length)+1)].path;
$("#abc").delay(1000).empty();
var prox_img = $('<img id="abcimg" height="300px" width="300px">');
prox_img.attr('src', new_img);
prox_img.appendTo('#abc');
}
to = setTimeout(function(){showNext()}, 2000);
}
$(document).keypress(function(e){
if ($(e.target).is('input, textarea')) {
return;
};
clearTimeout(to);
if (e.which === 97 || e.which === 108) {
setTimeout(function(){showNext();}, 1000);
}
});
});

Recursive function causes other calls to fire multiple times

I'm working on a quiz game, wherein the user is presented with a quote and has to guess the author:
function startGame(quotes) {
askQuestion(quotes[0], 0, 0);
function askQuestion(quote, question, score) {
var q = "<span class='quo'>“</span><em>" + quote.quote + "</em><span class='quo'>”</span>";
$('.choice').css('visibility', 'visible');
$('#questions').html(q);
$('.choice').click(function(e){
$('.choice').css('visibility', 'hidden');
e.preventDefault();
var nextq = (question + 1);
var btntxt = (nextq < number_of_questions ? 'Next...' : 'Final results');
if ($(this).attr('data-author') === quote.author) {
score++;
$('#questions').html('<h1>Correct.</h1><a class="btn next">' + btntxt + '</a>');
document.getElementById('win').play();
} else {
$('#questions').html('<h1>Wrong.</h1><a class="btn next">' + btntxt + '</a>');
document.getElementById('lose').play();
}
$('#questions').append('<h4>Score: ' + score + '/' + nextq + '</h4>');
$('.next').on("click", function(){
question += 1;
if (question < number_of_questions) {
askQuestion(quotes[question], question, score);
} else {
tallyScore(score);
}
});
});
}
}
When a question is asked, the askQuestion() function is called again if fewer than 6 questions have been asked.
Everything works great, but I'm having issues with the sound effects. If a user gets an answer right and then an answer wrong, both the "win" and "lose" sound effects are played simultaneously.
My guess is that this has something to do with my recursively calling askQuestion() -- it seems like the entire "history" of the function is looped through. I was having a similar problem earlier — on correct answers, the score global variable was incremented by the number of previously correct answers (instead of just by one).
Any idea how I can fix that? Thanks!
Edit: As requested, here's a JSfiddle.
easy fix actually. you are re-attaching the click listener over and over, so just remove it each time it gets set.
change
$('.choice').click(function (e) {
to
$('.choice').off().click(function (e) {
http://jsfiddle.net/NADYM/
Every time askQuestion is called, you add an event handler to the html elements. So when you click on the .choice element, multiple events are run.
Try giving a unique id to all generated element and use that id to attach event handlers.

How to detect one by one click interval with jQuery?

I have a little question, is there any option to detect clicks interval and set for them any condition. For example if i click one by one in one second interval i can do something, and if i click again after 0.5s i can make something else? I have no idea how to write this so i can't add any example on my code only this vision, also i can't find this solution in other therds, can you help me?
var clicks=0;
function myClickFunction(event){
clicks++;
}
$(function(){
$("#something").on("click",myClickFunction);
if (click == )){
}
});
});
Much thx for answer!
var lastClickTime;
function testTime(time){
switch (true) {
case (time < 500):
alert("less than 500ms!");
break;
case (time < 1000):
alert("less than 1000ms but not 500ms!");
break;
default:
alert("you sure click slow!");
break;
}
}
$("#something").on("click", function(e){
e.preventDefault();
var newTime = new Date();
if(lastClickTime !== undefined) {
testTime(newTime - lastClickTime);
}
lastClickTime = newTime;
});
This is a quick way to do it, but creates some globals. It should put you on the right path though!
A switch might help you keep track of all the different cases you want.
You can try to bind click and dblclick functions. See the docs and watch out this:
Note: Although demonstrated in the next example, it is inadvisable to
bind handlers to both the click and dblclick events for the same
element. The sequence of events triggered varies from browser to
browser, with some receiving two click events before the dblclick and
others only one. Double-click sensitivity (maximum time between clicks
that is detected as a double click) can vary by operating system and
browser, and is often user-configurable.
Source
You will have to manually track the time of the last click. Then every time your event handler is called compare the current time to the stored one.
Some pseudo code:
last_click_time = 0
onClick () {
if ( last_click_time == 0 ) {
// first click
last_click_time = time.now();
} else {
time_difference = time.now () - last_click_time;
if ( time_difference == 0.5 ) {
// half a second since last click
} else if ( time_difference == 1 ) {
// one second since last click
}
...
last_click_time = time.now ();
}
}
If you want exact control of timing you could just check the milliseconds on the current machine.
Link to fiddle
Html:
<a id="asd">yo</a>
Javascript:
$("#asd").click(function(){
var latest = $(this).data("time") || 0;
var now = +new Date();
var diff = now-latest;
if(diff < 500) alert("That was faster than 0.5 seconds!");
if(diff > 1000 && diff < 1500) alert("That was between 1.0 and 1.5 seconds.");
$(this).data("time",now);
});

Categories