How to proper use setTimeout with IE? - javascript

For a mockup-webpage used for research on interaction on websites, I created a mockup message-stream using JavaScript. This message stream should show images at pre-set intervals. In Chrome, this code posts the images below one another, at the pre-set intervals. In IE, only the first image is shown. I have already removed the passing of parameters in the window.setTimeout method, but am lost as to what else I need to do to satisfy IE. What else should I do to make all images appear in the pre-set intervals?
My script-section has several global variables:
var updateinterval = 400; // a multiplier (integer) used to
var scrollinterval = 5; // an interval (integer) to wait before scrolling
var point; // integer used to prevent using parameters in window.setTimeout
var interval = [0, 10, 40]; // array of integers creating diversity in intervals
var images = ["r1", "a1", "r2"];// array of strings referring to images to show
The following functions are present:
function trypost(){
point = point + 1;
if(point < interval.length){
//write the required image
document.writeln("<img src='images/"+images[point]+".png'/><br/>");
//time scroll to bottom
var stb = window.setTimeout(scrollToBottom, scrollinterval);
//time next post
var nextupdate = interval[point]*updateinterval;
var tp = window.setTimeout(trypost, nextupdate);
}
}
function scrollToBottom(){
window.scrollBy(0,document.body.scrollHeight);
}
function startpost(){
point = -1;
trypost();
}
window.onload = startpost;

You can use setInterval instead of repeatedly calling setTimeout, setInterval repeats until you call clearInterval.
Also as noted in the comments, document.writeln is from a different decade ;)
You can modify the DOM directly now.
var i=0;
var interval = [0, 10, 400, 10, 1000];
var images = [
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRTiW-7zqLiG1DNq4Tmt6x4j1iBc0FZRBpyYZtIXgDzUy_NHwTv",
"http://img2.wikia.nocookie.net/__cb20120327004334/mrmen/images/a/a0/MrMean.gif",
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRTiW-7zqLiG1DNq4Tmt6x4j1iBc0FZRBpyYZtIXgDzUy_NHwTv",
"http://img2.wikia.nocookie.net/__cb20120327004334/mrmen/images/a/a0/MrMean.gif",
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRTiW-7zqLiG1DNq4Tmt6x4j1iBc0FZRBpyYZtIXgDzUy_NHwTv"
];
for(var i=0; i<interval.length;i++)
var timer = setTimeout(function(img){
var newImg = document.createElement("IMG");
newImg.src = img;
document.getElementById('holder').appendChild(newImg);
}, interval[i], images[i]);
with this HTML
<div id='holder'>
</div>
Running demo
http://jsfiddle.net/W7QXH/4/

Related

Javascript to track amount of audio listened in Qualtrics

I am currently trying to use Javascript to record how much of an audioclip someone chooses to listen to on my Qualtrics survey. However, for some reason the code that I have keeps returning all 0s (both in the console log as well as in the embedded data file). I am still very new to Javascript and would greatly appreciate another set of eyes!
Qualtrics.SurveyEngine.addOnload(function() {
/*Place your JavaScript here to run when the page loads*/
function roundNumber(number, digits) {
var multiple = Math.pow(10, digits);
var rndedNum = Math.round(number * multiple) / multiple;
return rndedNum;
};
// variables to keep track of progress
var audioListened = 0;
var audioSkipped = 0;
var lastPosition = 0;
// initialize data to 0
Qualtrics.SurveyEngine.setEmbeddedData('totalTime', 0);
var audio_element = jQuery("audio.qmedia") // HTML audio element to track
// as the audio plays, we update the data with the total amount of time listened
audio_element.ontimeupdate = function(event) {
audioListened = event.currentTime - audioSkipped;
lastPosition = event.currentTime;
Qualtrics.SurveyEngine.setEmbeddedData('totalTime', roundNumber(audioListened, 2));
};
console.log(audioListened, audioSkipped);
// when participant skips time, we keep track of this to subtract from total time
audio_element.onseeked = function(event) {
audioSkipped = (event.currentTime - lastPosition); // adds fast-forwarding, subtracts rewinding from total
};
console.log(audioListened, audioSkipped);
});
A better way to handle this is to use the played property of the media element. It gives you a TimeRanges object that you can use to figure out what parts have been watched/listened to.
for (let i=0; i<audio.played.length; i++) {
// Logs something like: 0, 5.3333
console.log(audio.played.start(i), audio.played.end(i));
}
Here's a working JSFiddle example: https://jsfiddle.net/qhryjz82/

Javascript webpage stop functioning, unresponsive/crashes

I'm having an issue with a Javascript assignment task. Essentially what I'm trying to accomplish is a "screensaver" design loop using html5 canvas. When a button is clicked in html, it runs a function that uses random number generation to decide on a shape, location of the shape, shape fill colour and shape stroke style, draws the shape and then loops back around again. The issue I'm currently having is when the button is clicked, the page (I'm using both chrome and firefox) crashes. I understood that this may be because of the loop that I am using, so in response I put a 1.5 second delay on the function- however it still won't function. Any advice on how to improve this would be greatly appreciated.
Thanks :)
(I tried indenting code by 4 spaces, sorry wouldn't work for some reason..?)
HTML:
<button onclick="Screensaver()">Screensaver</button><br>
<div id="Screensaver">
<br><button onclick="screenstart()">Start Screensaver</button><br>
<br><button onclick="screenstop()">Stop Screensaver</button>
</div><br>
<canvas id="myCanvas" width="1000" height="600" style="border:1px solid #47bfff;">
</canvas>
JAVASCRIPT:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var gradient=ctx.createLinearGradient(0,0,canvas.width,0);
//screensaver variables
var loop = 0;
var shapenum = 0;
var fillnum = 0;
var stylenum = 0;
var SS = document.getElementById("Screensaver");
//rectangle
var rectx1 = 0;
var recty1 = 0;
var rectx2 = 0;
var recty2 = 0;
//triangle
var trix = 0;
var triy = 0;
//circle
var circx = 0;
var circy = 0;
var circr = 0;
//radius calculation
var sct = 0;
var scb = 0;
var scl = 0;
var scr = 0;
var scmax = 0;
//SCREENSAVER FROM HERE
SS.style.display = "none";
function Screensaver() {
if (SS.style.display === "" +
"none") {
SS.style.display = "block";
} else {
SS.style.display = "none";
}
}
//loop
function screenstart(){
loop = 1;
while (loop = 1){
setTimeout(screenloop(), 1500);
screenloop();
}
}
function screenstop(){
loop = 0;
}
function screenloop(){
randcal();
ifstatements();
}
//just calculations from here on down
As I said in my comment, the problem is in your screenstart function. There are actually quite a few things wrong in that, but the main one, which causes the crashing/freezing of your browser, is that the loop never actually ends. Javascript is single threaded, that is it can only do one thing at a time - and while the loop should stop when loop is 0 (or would if you'd correctly used == rather than =), this never happens because your code is stuck in a loop, executing screenloop continually (while also scheduling more and more calls to this function later, not that it can ever get to them).
There is a straightforward fix though. Cut down the function to just this:
function screenstart(){
loop = 1;
setInterval(screenloop, 1500);
}
And then do the following in screenloop:
function screenloop(){
if (loop == 1) {
randcal();
ifstatements();
}
}
The most important difference is that you simply schedule screenloop to run every 1.5 seconds, while allowing other events to happen, and other code to run if necessary, in between. Including the possibility of loop being set to 0. And every time screenloop runs, it checks this value, and only does its stuff if the value is 1.
Your current code basically spams the event loop with calls to screenloop().
setTimeout() just schedules a call to that function in 1.5s, but does not hold the loop until then. So basically your loop here just fills up the event loop until the browser crashes.
function screenstart(){
loop = 1;
while (loop = 1){
setTimeout(screenloop(), 1500);
screenloop();
}
}
For animations like yours you may want to look at requestAnimantionFrame(). On a very rough first attempt this could look like this (I did not look though all of your code, whether this actually will work. Depends on different factors, like how long each rendering loop takes etc):
let animationId;
function screenstart(){
randcal();
ifstatements();
animationId = requestAnimationFrame(screenstart);
}
function screenstop(){
cancelAnimationFrame( animationId );
}

jQuery 2.1.0 | Display elements incrementally

I need to display rows of buttons incrementally from top to bottom in a scrollable parent div. These rows of buttons are created using a replace method applied onto a text string converted to html. Since I can have 500+ more rows of buttons to create and display, the text-to-html conversion can take a few seconds or longer, which freezes the UI during conversion. The code below uses a setInterval which unlocks the UI and provides a cool way of "animating" the gradual display of button rows. Problem is, currently, the whole set of rows is repeated at each setInterval, that's not what I want, I can't figure out what to do next. I need each individual row to be displayed incrementally without repeating, from top to bottom, in the order provided by the string, until the length of array is met. The scrolable parent div is a fixed height of 300px. Maybe a Lazy loading method would be better? Any help solving this issue is appreciated.
DEMO fiddle
var placeholder = $('#placeholder');
placeholder.html(placeholdertohtml(placeholder));
function placeholdertohtml(placeholder){
placeholder.html(placeholder.text().replace(/((\d{2},\d{2}))/g, function(match, number){
var blocks = placeholder.text().split(',').length;
console.log(blocks);
var el = number.substr(0,5).split(',');
var prefix = el[0];
var suffix = el[1];
var t = setInterval(function(){
if (blocks) {
var content = '<p><button>'+prefix+'</button><button>'+suffix+'</button></p>';
$('#placeholder').append(content);
blocks--;
} else {
clearInterval(t);
}
}, 100);
}));
}
So, I reworked your code using for loops instead of the replace function to fix the problem.
Here is a working codepen
I basically made a loop that built up an array of html to add:
var numberOfPairs = placeholder.text().match(/((\d{2},\d{2}))/g).length;
var countdown = numberOfPairs;
var string = placeholder.text();
var elements = [];
for(var i = 0; i < numberOfPairs; i++) {
var pair = string.substring(5 * i + 1, (5 * i) + 6).split(',');
var prefix = pair[0];
var suffix = pair[1];
elements.push('<p><button>'+prefix+'</button><button>'+suffix+'</button></p>');
}
And then looped over the elements with your interval function to get the same "loading" effect:
var elementIndex = countdown;
var t = setInterval(function(){
if (countdown >= 0) {
$('#placeholder').append(elements[(countdown - elementIndex) * -1]);
countdown--;
} else {
clearInterval(t);
}
}, 100);

In box2dweb how to destroy created body

I create bodies at mouse click with this pretty standard way in box2dweb:
*stage.onMouseDown = function(){
var fixDef = new box2d.b2FixtureDef;
fixDef.density = 1;
fixDef.friction = 0.5;
fixDef.restitution = 0.7;
var bodyDef = new box2d.b2BodyDef();
bodyDef.type = box2d.b2Body.b2_dynamicBody;
bodyDef.position.x = mouseX /scale;
bodyDef.position.y = mouseY /scale;
fixDef.shape = new box2d.b2CircleShape(Math.random()*100/scale);
world.CreateBody(bodyDef).CreateFixture(fixDef);}
I really don't know how to insert a name or an Id for my created bodies (eventually I can add a var num++ at every creation). Also, I don't know how to get back my body through the id and call the method .DestroyBody to delete it specifically.
I'm at early stages with JavaScript and Objective C so methods and docs made for Actionscript make me nuts..
Thanks in advance.
Question update:
I found a way to get back my already created object, finding the one i want among all of them using this way:
note: myBody is global
myBody['enter'+prodNum] = bodyDef;
bodyDef.userData = prodNum;
myBody['enter'+prodNum].id = bodyDef.userData;
prodNum is a global var which has a "++" at every cycle. With this i can recall my body back using both the body's var name and the bodyDef.userData property.
with the following function, called in my init() that's executed through window.onload, i can, as console.log shows, change what i want of my retrieved body, however no changes are applied to the body in canvas, even if its property in log resulted modified i cannot notice any change on the screen.
function reduceObj(){
var itsMe;
itsMe = myBody.enter10;
var newPosX = itsMe.position.x;
itsMe.active = false;
itsMe.awake = true;
itsMe.linearVelocity.x = 2000;
itsMe.position.x = newPosX+500;
itsMe.fixedRotation=true;
itsMe.allowSleep=true;
console.log(myBody.enter10,itsMe,itsMe.id,'it s me');
}
Cannot get why all this happens.. plus i already set the step() function which should refresh my world every x milliseconds... Help pls
The CreateBody function should return a reference you can keep, to destroy the body later.
var mybody = CreateBody( bodyDef );
mybody.CreateFixture( fixDef );
You can't just set properties in a body to change it, you need to use the appropriate functions:
// later...
mybody.SetActive( false );
mybody.SetAwake( true );
var vel = mybody.GetLinearVelocity();
vel.x = 2000;
mybody.SetLinearVelocity( vel );
var pos = mybody.GetPosition();
pos.x += 500;
mybody.SetPosition( pos );
mybody.SetFixedRotation( true );
mybody.SetSleepingAllowed( true );
Please keep in mind that 500 units is half a kilometer, so that's probably not what you would want to be doing. Use meters for your dimensions, not pixels. A velocity of 2000m/s is around 7200km/h or mach 6 (as reference the fastest aircraft ever built does about mach 8, so this is also most likely not what you want). Take a look at this page for some other common gotchas: http://www.iforce2d.net/b2dtut/gotchas

JavaScript - Timer Initiation

I'm trying to make my enemy fire every second. Right now - it's every frame.
Within my enemy object, I have a piece of code that is initiated when the player is within range:
this.shotBullet = false;
var object = this;
object.Fire();
This is the enemy fire function:
this.Fire = function(){
console.debug("Firing | Shot: " + this.shotBullet);
if(!this.shotBullet){
if(this.weapon == "pistol")
PistolEnemy(this);
this.shotBullet = true;
}
};
And my PistolEnemy function:
PistolEnemy = function(operator){
var user = operator;
console.debug("user:" + user.tag);
var bulletDamage = 1;
var bulletSpeed = 20;
var b = new Rectangle( user.x + (user.width / 2) - 4, user.y + (user.height / 2) - 4, 8, 8);
var velocityInstance = new Vector2(0, 0);
velocityInstance.x = Math.cos(user.rotation) * bulletSpeed;
velocityInstance.y = Math.sin(user.rotation) * bulletSpeed;
var bulletInstance = new Bullet(velocityInstance, b, "Enemy", bulletDamage, "blue");
/*audioPistol.volume = 0.5;
audioPistol.currentTime = 0;
audioPistol.play();*/
user.bullets.push(bulletInstance);
user.shotBullet = true;
};
I've tried playing around with the 'setInterval', but it doesn't work well. Most of the times, it waits for a second, then sprays a load of bullets.
All I want it for a enemy bullet to initiate every second.
Thanks
var triggerID = window.setInterval(function(){firing_clock()},1000);
function firing_clock()
{
// this will execute once per second....sort of
}
Mozilla window.setInteral doc
So, one thing you should known is if your browser gets busy it will get 'late'. Mozilla used to have an extra non-standard parameter detailing "actual lateness", but it no longer does - but the point was that you are asking the browser to try to do something once per second, but if it gets busy it will get behind or skip a few rounds (how the browser handles it differs by browser).
Ideally what you would do here is register your enemy object with a list that firing_clock() would work through to dispatch firing commands to all live enemies. This cuts overhead by only using one global timer, rather than one timer per object on screen.
Try this with just one hard-coded enemy and see how it works. If it still doesn't work, then it's a bigger problem as there is no javascript "guaranteed accurate timer" that I'm aware of.
But it should work, so long as things don't get too intense on the client's CPU, and having one global timer for ship firing should allow you to have a good number of ships firing away without too much ill effect.

Categories