What i am trying to do
I am trying to make a number increase to a certain state when entering view. I am gonna be completely transparent with you. I have next to no website development knowledge, this is simply for a wordpress site where i couldnt find a plugin to do it for me + this gives more flexability. My knowledge simply lies elsewhere. So far i have created code that activates as soon as it enters a view, and it also counts up.
Da Problem
As you can see here "https://jsfiddle.net/yd81prgq/" this works already. I then tried putting the exact same code in wordpress on a element. Now i know that its the activatition part thats not working, since during testing i made it count up, but only as soon as the page loaded.
Code i am putting in wordpress
<h2 id="(id)">Count to ten when seen</h2>
<script>
var i = 0;
function increment() {
if (i<10) {
i++;
document.getElementById('(id)').innerHTML = i;
}
}
function startcount() {
setInterval('increment()', 50);
}
var observer = new IntersectionObserver(function(entries) {
if(entries[0].isIntersecting === true)
startcount();
}, { threshold: [1] });
observer.observe(document.querySelector("#(id)"));
</script>
Thanks in advance :)
You must to remove brackets from the id. Globaly, an id should have only letters, numbers, underscore and/or dash.
PS: java is not javascript ;)
var i = 0;
function increment() {
if (i<10) {
i++;
document.getElementById('id').innerHTML = i;
}
}
function startcount() {
setInterval('increment()', 50);
}
var observer = new IntersectionObserver(function(entries) {
if(entries[0].isIntersecting === true)
startcount();
}, { threshold: [1] });
observer.observe(document.querySelector("#id"));
<h2 id="id">Count to ten when seen</h2>
An alternative with less code for the same result:
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
async function counter() {
for (let i = 1; i <= 10; i++) {
await sleep(50);
document.getElementById('id').innerHTML = i;
}
}
counter();
<h2 id="id">Count to ten when seen</h2>
I found the issue. This puts my amount of my fault posts up to 2/2... The problem was that since the code checks when a certain element enters view wordpress is apparently build in a way that makes it happen when it exits view. Probably to do with some visuals vs. code happening in the background of wordpress. I dont know, but this was the issue. Thanks for the help recieved
For future reference here is the working code:
<h2 id="id" class="class">Make this count to ten</h2>
<script>
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
async function counter() {
for (let i = 1; i <= 10; i++) {
await sleep(50);
document.getElementById('id').innerHTML = i;
}
}
var observer = new IntersectionObserver(function(entries) {
if(entries[0].isIntersecting === true)
counter();
}, { threshold: [1] });
observer.observe(document.querySelector("#id"));
</script>
Related
I'm new here so spare me please.
I'm working on a little hobby project of mine where an application gathers telemetry which is recorded and saved at 60Hz (so 60 index in an array for each second). This is always the case.
I then want to 'play through' this array at the regular speed as if you're playing a video. But instead of seeing a video I want to show the current data from each index that you're at on the frontend.
The structure of the array is not made yet, but I assume I will just store and load a JSON file somewhere in the database. This then should be played through in my frontend. I've used Angular (6) to build that, so it would be great if it could be mixed with Angular (to keep track of the progress of which index you are now, and bind the values of the current index to the frontend).
Would it be easy to just use 0-1-2-etc for indexx, or maybe something like timestamps?
Any suggestions are welcome. The most important thing here is to keep it quick so you don't need a strong rig to play this.
Thanks in advance!
You need to use setInterval function in which you will iterate over the array according to your frequency. So if your frequency is 60hz that means you want to iterate to next element in array after every 1000 / 60 milliseconds
var data = [1, 2, 3, 4, 5]
var currentIndex = 0;
var interval = 1000 / 60
var id = setInterval(function() {
// do your thing
if(currentIndex == (data.length-1)) {
clearInterval(id)
} else {
currentIndex++
}
}, interval)
This is not a particularly iterating over array but rather doing some action after interval of time and then moving to next item and when you are done with array this will clear the interval. Perhaps linked list will be more helpful than array here
You can make yourself a simple runner for such things. It's basically a game engine and you need a game loop :)
Here's a naive example. Skipping most of error checking and validation here for brewity.
class TelemetryPlayer {
constructor(items, render, interval) {
// setup local data.
this.interval = interval;
this.items = items;
this.render = render;
this.startLoop();
}
// Loop simply initializes before the first run, and then runs the loop.
// Other places do the actual work.
startLoop() {
this._renderInProgress = false;
this._currentIndex = 0;
// save the interval reference if you wanna pause/reset later on.
this._interval = setInterval(this.doWork.bind(this), this.interval);
}
// here we perform the actual render.
doWork() {
if (this._renderInProgress) {
// previous render has not completed yet.
console.log('skip');
return;
}
console.log('Tick');
this._renderInProgress = true;
const item = this.items[this._currentIndex];
console.log('Tick');
// now, call your renderer, and update stuff when complete.
this.render(item)
.then(() => {
// Or this can be a callback or similar.
this._renderInProgress = false;
// Ready next item. Do not go out of array bounds.
this._currentIndex++;
if (this._currentIndex === this.items.length) {
this._currentIndex = 0;
}
});
}
// You can then add fun things like skip, pause, reset etc.
skip(item) {
if (item < 0 || item > this.items.length) {
return;
}
// pause first
this.pause();
this._currentIndex = item;
this.unpause();
}
//
reset() {
this.skip(0);
}
//
pause() {
this._interval = clearInterval(this._interval);
}
unpause() {
if (!this._interval) {
this._interval = setInterval(this.doWork.bind(this), this.interval);
}
}
// you can even add items later
addItem(item) {
this.items.push(item);
}
// or replace them.
replaceItem(item, index) {
this.items[index] = item;
// show the new item right away.
this.skip(index);
}
// or add an item to be played just once.
playOnce(item) {
this.pause();
this.render(item);
this.unpause();
}
}
Now here's an example usage. You can copy the code (both class above and the code block bellow) and paste it into console right here on StackOverflow to see it in work. You likely wanna do other things, but you'll get the gist.
let items = [ 100, 200, 300, 50, 100, 200, 300, 250 ];
const timeline = document.createElement('ul');
// maybe better do this by adding class and external stylesheet
timeline.setAttribute('style', 'padding: 15px; border: 1px solid gray; list-style-type: none; height: 500px; width: 100%; position: absolute; top: 0;overflow-y: scroll;')
document.body.appendChild(timeline);
let render = function(item) {
return new Promise(function (resolve) {
// run something (in) expensive with item now.
const li = document.createElement('li');
// again, better do this with class.
li.setAttribute('style', `display: inline-block; width: 25px; margin: 5px; background-color: #c1c1c1; height: ${item}px;`);
timeline.appendChild(li);
li.scrollIntoView();
// return when done.
resolve();
});
}
const player = new TelemetryPlayer(items, render, 1500);
// now you can do things like speed up etc.
// now you can do things like speed up etc.
function speedUp() {
// speedup 3 seconds after "playback" starts.
return new Promise((resolve) => setTimeout(() => {
player.pause();
player.interval = 600;
player.unpause();
resolve();
}, 3000));
}
function playOnce() {
// add item once, but not in the array we loop around in
return new Promise((resolve) => setTimeout(() => {
player.playOnce(1000);
resolve();
}, 3000));
}
// or add a few items that will be repeated.
function addItems() {
// add a super very high item 3 seconds after function call.
return new Promise((resolve) => setTimeout(() => {
player.pause();
player.addItem(400);
player.addItem(430);
player.addItem(420);
player.unpause();
// now rewind to this block. I am off by one, likely.
player.skipTo(player.items.length - 3);
resolve();
}, 5000))
}
speedUp()
.then(playOnce)
.then(addItems);
I am trying create a page that will run some given URLs, in time this will be a database populated list of URLs loaded into an array. I have it working using some code I have altered, however I don't want it to keep looping, I just want it to stop once the list is complete.
So basically loads first URL, waits .5 of a second after the pages loads, then moves onto the next until the list of URLS is finished.
However, my code keeps looping. How do I prevent this?
var urls1 =
[
'http://localhost:8500/SupportTools/t1.cfm',
'http://localhost:8500/SupportTools/t2.cfm',
'http://localhost:8500/SupportTools/t3.cfm',
'http://localhost:8500/SupportTools/t4.cfm',
'http://localhost:8500/SupportTools/t5.cfm'
];
function showUrl1(idx)
{
var f1 = document.getElementById("f1");
f1.onload = function()
{
var next = ++idx % urls1.length;
setTimeout(function()
{
showUrl1(next);
}, 500);
}
f1.src = urls1[idx];
}
function start()
{
showUrl1(0);
}
<iframe class="show1" id="f1" src="about:blank"></iframe>
The reason it's looping is because you're using the remainder operator %. Just check the to see if next is greater than or equal to the length and then don't call setTimeout(). I've changed the snippet so that you can see it output in the console but you should get the idea.
var urls1 = [
'http://localhost:8500/SupportTools/t1.cfm',
'http://localhost:8500/SupportTools/t2.cfm',
'http://localhost:8500/SupportTools/t3.cfm',
'http://localhost:8500/SupportTools/t4.cfm',
'http://localhost:8500/SupportTools/t5.cfm'
];
function showUrl1(idx) {
if (idx >= urls1.length) {
return;
}
console.log(urls1[idx]);
var next = ++idx;
setTimeout(function() {
showUrl1(next);
}, 500);
}
function start() {
showUrl1(0);
}
start();
Dont showUrl when the list ends
function showUrl1(idx)
{
var f1 = document.getElementById("f1");
f1.onload = function()
{
var next = idx === urls1.length? null: ++idx ;
setTimeout(function()
{ if(next != null){
showUrl1(next);
}
}, 500);
}
f1.src = urls1[idx];
}
I created a library to pop up some toast notifications and I tried to put a limit on the maximum notifications on screen.
I managed to extract the idea into a plunker (don't mind the code, it is only to solve the issue).
I have a function to create those toasts:
function createToast() {
var body = $document.find('body').eq(0);
var toast = {};
toast.id = index++;
toast.el = angular.element('<div class="toast">Toast ' + toast.id + '</div>');
toast.el = $compile(toast.el)($scope);
if (maxOpened && toasts.length >= maxOpened) {
remove(toasts[0].id);
}
toasts.push(toast);
$animate.enter(toast.el, body).then(function() {
$timeout(function() {
remove(toast.id);
}, 3000);
});
}
Basically it creates a new object with an el and then animates it out on the body. Notice that if the maxOpened is reached it removes the first one.
function remove(id) {
var toast = findToast(id);
if (toast) {
$animate.leave(toast.el).then(function() {
var index = toasts.indexOf(toast);
toasts.splice(index, 1);
});
}
function findToast(toastId) {
for (var i = 0; i < toasts.length; i++) {
if (toasts[i].id === id) {
return toasts[i];
}
}
}
}
Find the toast, animate the leave and then delete it.
If I do a $interval on them, let's say 600ms it works.
Try here: http://plnkr.co/edit/lDnT57FPadCt5Ir5wHuK?p=preview
If you lower it to something like 100ms it starts to break, not only ignoring the max but also leaving some orphan toasts that won't get deleted.
So I am not sure what could be a good solution here. My best guess is to provide a queue so I start to drain it as soon as a toast get removed but so far, I didn't make it.
The probably simplest solution would be to add a deferred to each toast and only start to animate the toast when the limit is not or no longer reached.
You start by adding a deferred and resolve it immediately, if the limit is not reached yet or the limit can be ignored:
toast.slotOpen = $q.defer();
toasts.push(toast);
if (maxOpened && toasts.length <= maxOpened || !maxOpened) { // i guess 0 or a falsy value means to ignore the limit
toast.slotOpen.resolve();
}
You only start the animation, when a slot is open:
toast.slotOpen.promise.then(function() {
$animate.enter(toast.el, body).then(function() {
The last thing to do is to resolve the deferred when a new slot gets opened after an old toast has been removed:
$animate.leave(toast.el).then(function() {
var index = toasts.indexOf(toast);
toasts.splice(index, 1);
if (maxOpened && toasts.length >= maxOpened) {
toasts[maxOpened - 1].slotOpen.resolve();
}
I have adjusted your code and created a new Plunker.
In an simple web app that i am building using underscore.js and jquery. For a list of all people ( js object ) I am filtering out list of all the places (js object) they visited. People list is a html table with a td having places image icon which on click displays list of all places they visited. Icon needs to be shown only for people who have visited at the least one place. The problem here is that people and places count comes around 2000, 100. So the code below executes 2000*100 combinations. The browser complains me of unresponsive script. Code is provided below
_.each(peopleList, function (person, index, list) {
//filter the respective places for people
var visitedPlaces = _.filter(places, function (place) {
return place.PeopleId == person.Id;
});
if (_.isEmpty(visitedPlaces)) {
$("a#" + place.PeopleId).remove();
}
});
Dead simple isn't it. For each person check if visited places has him tracked. How do i optimize the above code to unblocking and responsive. Tried putting in _.defer and _.delay at some places but no improvement
FWIW, here is how I would solve it in underscore.
function removeNonTravelers(people, visits) {
var travelers = _.pluck(visits, 'PeopleId'),
nonTravelers = _.reject(people, function (person) {
return _.contains(travelers, person.Id);
});
$(_.map(nonTravelers, document.getElementById)).remove();
}
http://jsfiddle.net/FWzeN/
My suggestion would be to drop underscore and use plain JS for this:
function removeNonTravelers(people, visits) {
var i, peopleId,
numPeople = people.length,
numVisits = visits.length,
index = {}, nonTravelers = [];
// index
for (i = 0; i < numVisits; i++) {
peopleId = visits[i].PeopleId;
if (!index.hasOwnProperty(peopleId)) {
index[peopleId] = 1;
} else {
index[peopleId]++;
}
}
// find HTML elements to remove
for (i = 0; i < numPeople; i++) {
peopleId = people[i].Id;
if (!index.hasOwnProperty(peopleId)) {
nonTravelers.push(document.getElementById(peopleId));
}
}
// remove them all at once
$(nonTravelers).remove();
}
This is reasonably fast. If I didn't make any mistake, your test case (2000 people, 100 places) times at more than 700 operations per second on my rather outdated laptop (DOM operations excluded).
Try for yourself: http://jsperf.com/where-not-exists-in-javascript
var hashMap = {};
_.each(places, function(place) {
hashMap[place.PeopleId] = place;
});
_.each(peopleList, function (person, index, list) {
//filter the respective project documents
var visitedPlaces = hashMap[person.id];
if (visitedPlaces) {
$("a#" + place.PeopleId).remove();
}
});
Thank you for taking the time to help me.
I am writing a game where an animated train icon moves along a given path to a destination, pausing at waypoints along the way. This is intended to give the impression of animation.
The game is coded in Facebook Javascript. I need to find a way to make the train icon pause for 1 second before moving on to the next waypoint. I hoped to find a function that would allow me to pause script execution for one second, but nothing like that seems to exist in JS. So I tried setTimeout, but my primary problem with this is twofold:
I need to pass an array into the callback function as an argument, and I can't figure out how to make setTimeout do this.
I finally succeeded in using setTimeout to execute my train animation code for 5 waypoints (I overcame the issue in 1 by using global variables). Unfortunately, it appears that all five calls to setTimeout got queued almost simultaneously, which resulted in waiting one second for the first setTimeout to fire, thenn they all fired at once ruining the illusion of train animation.
I've been battling this problem for six hours straight. It would be wonderful if someone could help me find a solution. Thanks!
Here's the code:
function myEventMoveTrainManual(evt, performErrorCheck) {
if(mutexMoveTrainManual == 'CONTINUE') {
var ajax = new Ajax();
var param = {};
if(evt) {
var cityId = evt.target.getParentNode().getId();
var param = { "city_id": cityId };
}
ajax.responseType = Ajax.JSON;
ajax.ondone = function(data) {
var actionPrompt = document.getElementById('action-prompt');
actionPrompt.setInnerXHTML('<span><div id="action-text">'+
'Train en route to final destination...</div></span>');
for(var i = 0; i < data.length; i++) {
statusFinalDest = data[i]['status_final_dest'];
//pause(1000);
gData = data[i];
setTimeout(function(){drawTrackTimeout()},1000);
if(data[i]['code'] == 'UNLOAD_CARGO' && statusFinalDest == 'ARRIVED') {
unloadCargo();
} else if (data[i]['code'] == 'MOVE_TRAIN_AUTO' || data[i]['code'] == 'TURN_END') {
//moveTrainAuto();
} else {
// handle error
}
mutexMoveTrainManual = 'CONTINUE';
}
}
ajax.post(baseURL + '/turn/move-train-final-dest', param);
}
}
function drawTrackTimeout() {
var trains = [];
trains[0] = gData['train'];
removeTrain(trains);
drawTrack(gData['y1'], gData['x1'], gData['y2'], gData['x2'], '#FF0', trains);
gData = null;
}
Typically this would be done by creating an object (say called myTrain) that has all its own data and methods, then call a myTrain.run mehod that looks to see where the train is. If it's between two stations, it calls itself with setTimeout and say a 50ms delay. When it reaches a station, it calls itself in 1000ms, creating a 1 second pause at the station.
If you queue the setTimeouts all at once, you run the risk of them all being delayed by some other process, then all running at once.
Hey, bit of fun (careful of wrapping). Needed a bit of practice with good 'ole prototype inheritance:
<!-- All the style stuff should be in a rule -->
<div style="position: relative; border: 1px solid blue;">
<div id="redTrain"
style="width:10px;height:10px;background-color:red; position:absolute;top:0px;left:0px;"></div>
</div>
<script type="text/javascript">
// Train constructor
function Train(id) {
this.element = document.getElementById(id);
this.timerId;
}
// Methods
// Trivial getPos function
Train.prototype.getPos = function() {
return this.element.style.left;
}
// Trivial setPos function
Train.prototype.setPos = function(px) {
this.element.style.left = parseInt(px,10) + 'px';
}
// Move it px pixels to the right
Train.prototype.move = function(px) {
this.setPos(px + parseInt(this.getPos(),10));
}
// Recursive function using setTimeout for animation
// Probably should accept a parameter for lag, long lag
// should be a multiple of lag
Train.prototype.run = function() {
// If already running, stop it
// so can interrupt a pause with a start
this.stop();
// Move the train
this.move(5);
// Keep a reference to the train for setTimeout
var train = this;
// Default between each move is 50ms
var lag = 50;
// Pause for 1 second each 100px
if (!(parseInt(this.getPos(),10) % 100)) {
lag = 1000;
}
train.timerId = window.setTimeout( function(){train.run();}, lag);
}
// Start should do a lot more initialising
Train.prototype.start = function() {
this.run();
}
// Stops the train until started again
Train.prototype.stop = function() {
if (this.timerId) {
clearTimeout(this.timerId);
}
}
// Set back to zero
Train.prototype.reset = function() {
this.stop();
this.setPos(0);
}
// Initialise train here
var myTrain = new Train('redTrain');
</script>
<p> </p>
<button onclick="myTrain.start();">Start the train</button>
<button onclick="myTrain.stop();">Stop the train</button>
<button onclick="myTrain.reset();">Reset the train</button>
To pass arguments, this might help you:
setTimeout(function() {
(function(arg1, arg2) {
// you can use arg1 / arg2 here
})('something', 123);
}, 1000);
Or, if you use a defined function:
setTimeout(function() {
someFunction('something', 123);
}, 1000);
It basically starts a timeout; after one second the function is invoked with the specified arguments.
How about using OO principles to simplify the problem? Create an "object" Train which has the following methods:
//train obj
function Train(){
this.isOnWaypoint = function(){
return calculateIsWayPoint()
}
}
//main logic
var train = new Train()
var doneWaiting = false
var doneWaitingTimeout = undefined
var gameLoop = setInterval(1000,function(){
...
if(train.isOnWaypoint() && !doneWaiting){
if(doneWaitingTimeout == undefined){
setTimeOut(5000,function(){
doneWaiting = true
doneWaitingTimeout = undefined
})
}
}
...
})
Here's the solution I finally came up with:
function drawTrackTimeout() {
if(gData != null && gIndex < gData.length) {
var trains = [];
trains[0] = gData[gIndex]['train'];
removeTrain(trains);
drawTrack(gData[gIndex]['y1'], gData[gIndex]['x1'], gData[gIndex]['y2'], gData[gIndex]['x2'], '#FF0', trains);
statusFinalDest = gData[gIndex]['status_final_dest'];
if(statusFinalDest == 'ARRIVED') {
unloadCargo();
} else if (gData[gIndex]['code'] == 'MOVE_TRAIN_AUTO' || gData[gIndex]['code'] == 'TURN_END') {
//moveTrainAuto();
} else {
// handle error
}
gIndex++;
} else {
clearInterval(gIntid);
gIntid = null;
gData = null;
gIndex = 0;
}
}
function myEventMoveTrainManual(evt, performErrorCheck) {
//debugger;
if(mutexMoveTrainManual == 'CONTINUE') {
var ajax = new Ajax();
var param = {};
if(evt) {
var cityId = evt.target.getParentNode().getId();
var param = { "city_id": cityId };
}
ajax.responseType = Ajax.JSON;
ajax.ondone = function(data) {
var actionPrompt = document.getElementById('action-prompt');
actionPrompt.setInnerXHTML('<span><div id="action-text">'+
'Train en route to final destination...</div></span>');
gData = data;
gIndex = 0;
gIntid = setInterval(function(){drawTrackTimeout()},1000);
}
ajax.post(baseURL + '/turn/move-train-final-dest', param);
}
}