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);
}
});
});
Related
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.
I was working on Babylon.js when I wanted to make a jump. I found a website and copied it jump code and it works perfectly! But I want to make a real game and I don't want people spamming spacebar to go flying. How could I make it have a few second delay so no one can keep on spamming spacebar and go flying?
function jump(){
camera.cameraDirection.y = 2;
}
document.body.onkeyup = function(e){
if(e.keyCode == 32){
//your code
console.log("jump");
setTimeout(jump(), 1000);
}
}
Link to my game
https://playground.babylonjs.com/#JCE1G3
One option would be to create a persistent boolean variable such as justJumped, and only jump if justJumped is false. When jumping, set justJumped to true, and create a timeout that resets it back to false after the duration of the jump, which looks to be a bit less than half a second:
let justJumped = false;
document.body.onkeyup = function(e) {
if (e.keyCode == 32 && !justJumped) {
justJumped = true;
setTimeout(() => justJumped = false, 400)
console.log("jump");
jump();
}
}
Also note that setTimeout(jump(), 1000); probably isn't doing what you're thinking it does - it invokes jump immediately. If you wanted to call the jump function after 1000ms, just pass the function name itself:
let justJumped = false;
document.body.onkeyup = function(e) {
if (e.keyCode == 32 && !justJumped) {
justJumped = true;
setTimeout(() => justJumped = false, 1400)
console.log("jump");
setTimeout(jump, 1000);
}
}
You can also use other library to achieve it. My favorite one is lodash. You can use _.throttle
var throttled = _.throttle(jump, 1000);
document.body.onkeyup = function(e) {
if (e.keyCode == 32) {
throttled()
}
}
if you don't want to jump immediately after the first key up. you can add option trailing: false
var throttled = _.throttle(jump, 1000, { 'trailing': false });
window.addEventListener('keydown', function(event) {
onKeyDownHandler(event);
}, false);
function onKeyDownHandler(e)
{
var focus_id = e.target.id;
switch (e.keyCode) {
case 13: // enter
if(focus_id == "Text1")
{
alert("function 1");
}else if(focus_id == "Text2")
{
alert("function 2");
}else if(focus_id == "Text3")
{
alert("function 3");
}
return;
}
}
is there anyway i can delay or make sure user dont spam by clicking the enter , how do i set keypress delay on my enter button ? which is the best way set delay timer or remove EventListener?
You can use the jQuery throttle/debounce plugin to only handle call your function when there is a pause in keyDown events.
You can prevent the default action for a period of time after the last Enter keypress:
window.addEventListener('keydown', onKeyDownHandler, false);
var lastEnter = null;
function onKeyDownHandler(e) {
var focus_id = e.target.id;
switch (e.which || e.keyCode) { // Note the e.which, for x-browser compat
case 13:
if (lastEnter && Date.now() - lastEnter < 5000) {
e.preventDefault();
return;
}
lastEnter = Date.now();
// Enter key processing...
break;
// ...other keys...
}
}
Or using jQuery (you've tagged your question jquery, but don't appear to be using jQuery in your code):
$(window).on("keydown", function(e) {
onKeyDownHandler(e);
});
var lastEnter = null;
function onKeyDownHandler(e) {
var focus_id = e.target.id;
switch (e.which) { // jQuery normalizes this for you
case 13:
if (lastEnter && Date.now() - lastEnter < 5000) {
e.preventDefault();
return;
}
lastEnter = Date.now();
// Enter key processing...
break;
// ...other keys...
}
}
Side notes:
Since the return value of an addEventListener callback is completely ignored and addEventListener calls the handler with just a single argument, if you're not using this within the handler (as you appear not to be), there's no need to wrap a function around onKeyDownHandler; just use it directly.
Some browsers use which for the keycode, others use keyCode, which is why I used e.which || e.keyCode in the switch. JavaScript's curiously-powerful || operator will use e.which if it's not falsey, e.keyCode otherwise.
You can create a timeout on enter press, and on another enter press, overwrite that previous timeout with the new one. That means that if you for example press enter again before the first timeout has ended, that first timeout will be overwritten by a new one, so that you get a new x amount of time before the actual timeout is executed. This works until infinity.
Example:
var keyup_timeout;
var timeout_delay_in_ms = 500;
element.on('keyup', function(e) {
e.preventDefault(); // Prevent default enter press action.
var enter_pressed;
if (e.which === 13) {
enter_pressed = true; // Just an example to illustrate what you could do.
}
if (enter_pressed) {
clearTimeout(keyup_timeout); // Clear the previous timeout so that it won't be executed any more. It will be overwritten by a new one below.
keyup_timeout = setTimeout(function() {
// Perform your magic here.
}, timeout_delay_in_ms);
}
});
When i press spacebar, the function shoot executes.
window.onkeydown=function(e){
var which = e.keyCode;
if(which == 32){
shoot();
}
}
If you hold space down, shoot calls many times in a row. I only want the function to execute once every 500ms.
(function($){
var lazerCharging = false,
lazerChargeTime = 500; // Charge time in ms
function handleKeyPress(e){
if(e.keyCode == 32){
shoot(lazerChargeTime);
}
}
function shoot(chargeTime){
if(!lazerCharging){
lazerCharging = true;
$("body").append("pew<br/>");
setTimeout(function(){
lazerCharging = false;
}, chargeTime)
}
}
$(window).on("keydown", handleKeyPress);
})($);
Here's a jsfiddle
You'll want to "debounce"
Using jQuery throttle / debounce, you can pass a delay and function to
$.debounce to get a new function, that when called repetitively,
executes the original function just once per "bunch" of calls.
This can be especially useful for rate limiting execution of handlers
on events that will trigger AJAX requests. Just take a look at this
example to see for yourself!
Ben Alman did the hard work for you here: http://benalman.com/code/projects/jquery-throttle-debounce/examples/debounce/
Essentially a debounce as MattC suggested. Store the time the function was called last and make sure 500 ms has passed. Also you probably should be using .addEventListener instead of window.onkeydown
(function() {
var lastCallTime = 0;
window.onkeydown = function(e){
var now = Date.now();
if(e.keyCode == 32 && now - lastCallTime > 500) {
shoot();
lastCallTime = now;
}
}
});
I doubt it's guaranteed that keydown/keypress events are always fired continuously. It may depend on the browser, operating system settings, etc. Even if they are, "fire rate" may fluctate. You probably don't want this.
I think a better idea would be to create a timer that's started when the first keydown event is fired, and stopped on keyup event.
http://jsfiddle.net/kPbLH/
var fireTimer = null;
function fire() {
// do whatever you need
}
document.addEventListener("keydown", function(e) {
if (e.keyCode == 32 && fireTimer === null) {
fire(); // fire immediately...
fireTimer = setInterval(fire, 500); // ...and 500ms, 1000ms and so on after
}
});
document.addEventListener("keyup", function(e) {
if (e.keyCode == 32 && fireTimer !== null) {
clearInterval(fireTimer);
fireTimer = null;
}
});
I've run into an issue using jPlayer and I thought after searching a few places that it would be used commonly enough on here and someone is bound to have run into a similar issue.
I am using the jPlayer to play through audio files and my users would like the functionality to fast-forward and rewind via specific hotkeys. I noticed that jPlayer doesn't offer this functionality out of the box (it only handles fast-forward and rewind through clicking on the progress bar currently)
Ideally - all that would be necessary would be for a single keypress to toggle the fast-forwarding (or rewinding). When the same key is pressed again, the audio file would begin playing based on the current location.
I decided to implement my own solution to this, which currently seems to work just fine. I thought I would share it in case anyone else encounters such an issue.
Pardon the crude implementation. It was just a proof of concept:
Necessary Javascript:
//Handles the key down event (so the user can hold a key down to continue)
$(document).keydown(function (e) {
//Rewind
if (e.keyCode == 37 && (!rewinding)) {
rewinding = true;
//Pause the player
$("#player").jPlayer("pause");
RewindTrack();
rwaction = window.setInterval(function () { RewindTrack() }, 500);
}
else if (e.keyCode == 39 && (!fastforward)) {
fastforward = true;
//Pause the player
$("#player").jPlayer("pause");
FastforwardTrack();
ffaction = window.setInterval(function () { FastforwardTrack() }, 500);
}
});
//Ends the action
$(document).keyup(function (e) {
//Rewind
if (e.keyCode == 37) {
rewinding = false;
window.clearInterval(rwaction);
$("#player").jPlayer("pause");
}
else if (e.keyCode == 39) {
fastforward = false;
window.clearInterval(ffaction);
$("#player").jPlayer("pause");
}
});
//Related function
function GetPlayerProgress() {
return ($('.jp-play-bar').width() / $('.jp-seek-bar').width() * 100);
}
//Handles rewinding
function RewindTrack() {
//Get current progress and decrement
var currentProgress = GetPlayerProgress();
//Rewinds 10% of track length
var futureProgress = currentProgress - 10;
//If it goes past the starting point - stop rewinding and pause
if (futureProgress <= 0) {
rewinding = false;
window.clearInterval(rwaction);
$("#player").jPlayer("pause", 0);
}
//Continue rewinding
else {
$("#player").jPlayer("playHead", parseInt(futureProgress, 10));
}
}
//Fast forwards the track
function FastforwardTrack() {
//Get current progress and increment
var currentProgress = GetPlayerProgress();
//Fast forwards 10%
var futureProgress = currentProgress + 10;
//If the percentage exceeds the max - stop fast forwarding at the end.
if (futureProgress >= 100) {
fastforward = false;
window.clearInterval(ffaction);
$("#player").jPlayer("playHead", parseInt($('.jp-duration').text().replace(':', '')));
}
else {
$("#player").jPlayer("playHead", parseInt(futureProgress, 10));
}
}
Working Demo (Use left arrow for rewind and right for fast-forward)