I have 2 audio files, this.audio1 and this.audio2. For each trial, the user is supposed to choose whether the two files are the same or different. So, I want to play the two audio files one after each other, wait for the use to click the same or different button, and then go to the next trial. This is my code so far for playing the audio files:
for (var i = 0; i < this.numTrials; i++){
var audio1; // these are actually initialized, obviously, in my code
var audio2;
var noRepeat = true;
audio1.addEventListener('ended', function () {
if (noRepeat) {
audio2.play();
noRepeat = false;
}
});
audio2.addEventListener('ended', function () {
game.audioComplete = true;
});
audio1.play();
// here, I want to add code so it waits until a button is pressed before moving on
}
I tried an eventListener where my comment is, which didn't work (it just kept looping through anyway). Does anyone have any suggestions for what I could use to "pause" the loop and wait for the button press, and then have it move on?
Thanks!
Something like this, this is the same as what #fixatd commented.
var globalPlayCount = 0;
...
function play() {
if(globalPlayCount < this.numTrials) {
globalPlayCount++:
audio1.play();
}
}
...
Fire play() on your button click. Just make sure you have both your event listeners intact.
Related
I am trying to increase a counter each time a button get clicked.
Everything is working, except the increment (keep having "odd" as an alert).
gwd.auto_Unfold_tapareaAction = function(event) {
var clickarea = document.getElementById('unfold_taparea');
var elementClicked = 0;
function handleIncrement() {
elementClicked++;
};
clickarea.addEventListener('click', handleIncrement, true);
if (elementClicked % 2 == 0) {
alert("odd");
} else {
alert("even");
};
};
If anyone knows how to do it, I'll be very grateful.
Thank you,
Loudvich.
Try placing the alert block inside the handleIncrement function at the end.
My best guess is the alert block is not running after the counter is incremented.
Edit: it looks like this function is running every time some other element is clicked.
Your issue is that you are creating the new click listener every time the gwd.auto_Unfold_tapareaAction runs.
Since it's newly created every time, it increments once, then alerts.
Try running this whole block on load or something that only runs one time.
I've successfully navigated to the corresponding page where I want to select multiple elements and click a button to confirm. The selection of the elements work, I've confirmed this with a screenshot, but clicking the button in nightmare does not work. When I run the segment in the console, everything works fine. The button has a randomly defined ID, and everything else except the innerHTML of the button is not unique, so I iterate over all buttons to match it based on content.
It's this snippet that's relevant.
.evaluate(function(){
//Select all the "elements" for room.
var elemArr = document.getElementById("L210").getElementsByTagName("td");
document.getElementById("resRoom").innerHTML = "L210";
document.getElementById("resStartTime").innerHTML = "08:00";
document.getElementById("resEndTime").innerHTML = "19:00";
for(var i = 0; i < elemArr.length; i++){
elemArr[i].className += " selected"
}
//Here select and click the button
var bTags = document.getElementsByTagName("button");
var searchText = "Confirm";
for (var i = 0; i < bTags.length; i++) {
if (bTags[i].innerHTML == searchText) {
bTags[i].click();
break;
}
}
})
Without seeing your full code I can't say for sure, but the most likely answer is that in the context of evaluate your nightmare code ( the .click() ) won't run because it doesn't have access to the original nightmare function. You would either need to use nightmare.click("bTags[i]") or use
.then(function(result){
if(result === "Confirm"){
nightmare.click("bTags[i])"
}
On top of all this - you're using a for loop to call a nightmare action! This is going to cause some issues. Nightmare issues promises before executing. This means you're attempting to run multiple electron instances at the same time, because the promises execute concurrently with the for loop. Rather than queue up - they fight for dominance, causing a crash. You should probably use a generator to manage async code such as vo or co.
Resources:
Common nightmare.js pitfalls
onclick="HandleAction(\'playnow\');HandleAction(\'stop\');"
Performs the two functions simultaneously. No good since it just does the action \playnow\ and then \stop\ immediately after.
I need onClick to perform the function every other click.
Click 1: \playnow\
Click 2: \stop\
Click 3: \playnow\
etc.
Is there a simple way to achieve this?
Define a var which holds the current state:
var state = false;
function handleAction() {
if (state == false) {
// stuff for 'playnow' action
state = true;
return;
}
if (state == true) {
// stuff for 'stop' action
state = false;
return;
}
}
Declare a global variable and interchange it depending on what is passed:
var switch = "playnow";
function handleAction(a) {
if a === switch {
alert(a);
}
else {
alert(a);
switch = a;
}
}
See my answer to this question.
Lets make a function called toggle that lets you specify actions that happen every other click
var toggle = function (a, b) {
var togg = false;
return function () {
// passes return value back to caller
return (togg = !togg) ? a() : b();
};
};
We would then setup our click handler like this
button.addEventListener('click', toggle(function () {
//play now logic
}, function () {
// stop logic
}));
Your click handler now alternates between the first and second functions with every click.
Yes. You could do something like
var play=false,
button = document.getElementById('playbtn'),
audiotag = document.getElementById('audio');
button.addEventListener('click', function(){
if(play){
audiotag.pause()
play=true;
}else{
audiotag.play();
play=false;
}
})
to make this work you could use html like this:
<div id="audioplayer">
<button id="playbtn">play</button>
<audio id="audiotag" src="path/to/audiofile"></audio>
</div>
so you would add the audio html like above and use getElementById to get each element in javascript. after that you attach an event listener to listen for the click event and call the handler which is the anonymous function. Inside that function, you can use the native play and pause methods directly on the audio object to stop audio when it's playing and then play it again when it's stopped.
there are other attributes you can add to the audio tag to start it playing as soon as the page loads. When you click the button, the play variable is set to true so it will pause on the first click and then sets that to false. A subsequent click will play it again and set the variable to true again and so on
Tyr gave you an answer how to solve your problem. Here you go some notes which can help you design better code.
If you have i.e. one big animation and one button on your web, your code is perfectly ok, to keep the code simple is a good idea. But if you have something like this
<button onclick="...">Animation A</button>
<button onclick="...">Animation B</button>
Then you need better design. If you insert state global var into HandleAction, you break low coupling, HandleAction is bound to your single event and can't be reused elsewhere.
It is good to ask yourself What does this function do? In the first case, it is useful to choose better name, like HandleIntroAnimation. If it handles (any) animation, then it is good to specify it in the parameter.
function HandleAnimation(animation, play) {
if(play) ... // playnow
else ... // stop
}
This indeed does what the name tells. To use it in your code, write a proxy function:
<script>
// If you enhance your web, you only alter this code.
// HandleAnimation stays the same, regardless the changes.
var animationStatus = {}
function ToggleAnimation(animation) {
animationStatus[animation] = !animationStatus[animation];
HandleAnimation(animation, animationStatus[animation]);
}
</script>
<button onclick="toggleAnimation(this)">Animation A</button>
<button onclick="toggleAnimation(this)">Animation B</button>
Finally, you could completely decouple HTML and JS:
animations.js
window.addEventListener("load",function() {
// HandleAnimation and ToggleAnimation definitions goes here
// to avoid collisions in global namespace
var anims = document.getElementsByClassName("animation");
for(var i=0; i<anims.length; ++i) anims[i].addEventListener("click",function() {
ToggleAnimation(anims[i]);
});
});
your html
<script src="animations.js"></script>
<button class="animation">Animation A</button>
<button class="animation">Animation B</button>
and you have animation framework: every element with animation class magically toggles its animation. The animation data could be provided in data-* attribute, data-animation in this case.
Then you can provide it as open-source on github or use someone elses open code to fill the missing parts in your code where you were too lazy to code it yourself. Since many wheels were already invented, the only thing you need to code is usually proxy functions. That's how coders save each others time. Happy coding.
I have a series of buttons that fire the list function when they are clicked. The list function itself contains an AJAX request and a bunch of other stuff before and after which loads in a separate section of the page.
var list = function() { }
$$('.buttons').addEvent('click', list);
Everything works fine if I wait for list to complete before clicking on another button. However, if I click on two buttons quickly, the page will start to return incorrect responses. In fact, it appears as though the responses get out of sync by 1. So if I quickly click on button A then button B, it will load response A. Then if I click (much later) on button C, it will load response B.
There are two ways I can see to solve this:
1) Remove the click event from other buttons when any button is clicked and then restore it when list is complete. Unfortunately, I have tried placing $$('.buttons').removeEvents() at the top of the list function and then $$('.buttons').addEvent('click', list); at the bottom but this has no effect on the problem.
2) Chain the click events so that list is only ever executed when the preceding list has finished.
So can anybody tell me how to get the second solution working? Additionally, if anybody knows why the first solution doesn't work and why I get the weird delayed AJAX response behaviour, that would be great!
The first solution doesn't work because events on an element are fired in order, but are executed asynchronously. You'll need to setup a queue of callbacks that you can process when the event is triggered.
Here's the basic idea:
addQueuedEvent = function(node, event, callback) {
if ( typeof callback != "function" ) {
throw "Callback must be a function";
}
event = event.toLowerCase();
var eventQueue = "_" + event;
if ( !node.hasOwnProperty(eventQueue) ) {
node[eventQueue] = [];
}
node[eventQueue].push(callback)
};
processEventQueue = function(node, event) {
var eventQueue = "_" + event;
if ( node.hasOwnProperty(eventQueue) ) {
for ( var i=0, l=node[eventQueue].length; i<l; ++i ) {
node[eventQueue][i]();
}
}
};
And the usage:
var someElement = $("#some-element");
addQueuedEvent(someElement, "click", callback1);
addQueuedEvent(someElement, "click", callback2);
addQueuedEvent(someElement, "click", callback3);
someElement.addEvent("click", function() {
processEventQueue(this, "click");
});
The syntax checks out, but this is not tested. Hope that helps.
i would personally just set a global / scoped variable in your class or whatever - something like 'isClicked = false'.
then simply check at the the the click event function, something like:
var isClicked = false, click = function() {
if (isClicked)
return false;
isClicked = true;
// ... do stuff, chained or otherwise...
// when done, make click function work again:
isClicked = false; // you can do this onComplete on the fx class also if you use it
};
i would go against chaining events with effects - if you have an animation going on, simply wait for it to end--otherwise it can get messy for any trigger happy user that thinks double clicking is the way to go. an alternative is to stop / cancel any effects that are taking place on a new click. for instance, you can stop any tweens etc through FX by something like:
if (isClicked === true) fxinstance.cancel();
http://mootools.net/docs/core/Fx/Fx
the other thing you can do is look at the mootools .chain class
http://mootools.net/docs/core/Class/Class.Extras#Chain
and also, on any fx instances, you can pass on link: "chain" and simply queue them up.
good luck
In my web app, I use the onkeydown event to capture key strokes.
For example, I capture the 'j' key to animate a scroll down the page (and do some other stuff meanwhile).
My problem is the user might keep the 'j' key down to scroll further down the page (this is equivalent to fast multiple key strokes).
In my app, this result in a series of animations that doesn't look that good.
How can I know when the key has been released, and know the amount of key stokes I should have captured? This way I could run one long animation instead of multiple short ones.
Building on #JMD:
var animate = false;
function startanimation()
{
animate = true;
runanimation();
}
function stopanimation()
{
animate = false;
}
function runanimation()
{
if ( animation_over )
{
if ( !animate )
{
return;
}
return startanimation();
}
// animation code
var timeout = 25;
setTimeout(function(){runanimation();},timeout);
}
document.onkeydown = startanimation;
document.onkeyup = stopanimation;
You'll need to add some checks for starting/ending animations, however.
Edit: added a return to the JS; would've recursed endlessly.
Rather than trying to stack up the animations, you could start an animation on keyDown, and if at the end of the animation you haven't yet received keyUp then start another animation. As soon as you reach the end of an animation and you do have keyUp then you're done.
setTimeout returns a timer ID. So what I would do is after you receive a keyDown event, I would start a timer with a very short wait period, like so:
var globalTimerId = -1;
var keyDownCount = 0;
function handleKeyDown(e) {
if (globalTimerId != -1) {
clearTimeout(globalTimerId);
keyDownCount++;
}
/* 500 means 1/2 a second, adjust for your needs */
globalTimerId = setTimeout(handleKeyDownAfterWait, 500);
keyDownCount = 1;
}
function handleKeyDownAfterWait() {
globalTimerId = -1;
/* keyDownCount will have the number of times handleKeyDown was called */
}
So the idea is that each time a keyDown event is received, the timer is cleared (assuming it hasn't elapsed yet) and restarted. If the timer expires, the user has let go of the key and you can handle that in handleKeyDownAfterWait.
There may be other more elegant solutions with jQuery or another JS library however. This is just something quick and dirty (and possibly buggy ;))
you can try using my repsonsiveness plugin see:
http://notetodogself.blogspot.com/2008/12/jquery-responsiveness-plugin-for-fast.html
see the demo here:
http://doesthatevencompile.com/current-projects/jquery.responsiveness/
the one where you type stuff fast. you can adapt that to your needs. like so:
the animation event will be bound with the plugin, and execute when the user stops doing something fast. you can count how many times he does the fast thing by normal binding of the event.