Watch a DOM property - javascript

I'm trying to monitor the DOM element on a third-party website. The element is non-existent until a countdown timer reaches, then it is created.
I've had some success playing around with: document.getElementsByClassName('countdown ng-hide').length
when it changes from 0 to 1 I want to effect a function.
How would I do this? I've tried using a Mutation Observer but it won't let me observe a null node.
Thanks!
EDIT: This is what I've got so far.
var timesDone = 0;
var songID = 0;
function clickit(xsongID) {
if(document.getElementsByClassName('lottery-countdown ng-hide').length == 1) {
document.getElementsByClassName('media submission ng-scope')[xsongID].click(); songName = document.getElementsByClassName('media-title submission-name ng-binding')[xsongID].outerHTML; timesDone++; }
}
setInterval(clickit, 29900, songID);

I did this recently by setting up an Interval function like this :
var timesTest = 0;
var checkExists = setInterval(function() {
if ($('.yourClassElement').length) {
// ok element found : do your stuff and clear the Interval...
// stuff...
clearInterval(checkExists);
timesTest = 0;
}
timesTest++;
// I won't let this run more than 5 seconds, so :
if (timesTest*100 > 5000)
clearInterval(checkExists);
}, 100);

Related

How to expire session if a have a setinterval js running?

I've a page with a chart on it, the chart is update via JS using a setinterval of 20 seconds, now my session is not expiring, i've searched over the internet and didn't found something like this, how i would detect user inactivity with this chart running?
setInterval(function(){
$.ajax({
type:'POST',
url: 'php/grafico.php',
success: function(e){
e = JSON.parse(e);
var linhas = e['linhas'];
var label = new Array();
for(var i = 1;i <= linhas; i++){
label.push('');
}
var preco = new Array();
for(var i = 1;i <= linhas; i++){
preco.push(e[i]['preco']);
}
addData(chartGraph, preco, 0, label);
}
});
var base = $('#Layer_base').height();
$('#menuzinhu').css('min-height',base+'px');
}, 20000);
heres the setinterval function, how it will update the session so it will never expire, but i need it to expire.
Thanks in adv.
You could create a timeout, which is set to fire, the value of the time being how long you should wait for user input/events before deeming them inactive. You'd then reset the timeout whenever an event is triggered by the user, where you define what events signify user activity.
See below for an example, this is untested, but should see you on the right track.
var inactiveTime = 30000; //30 seconds
var interactionEvents = [
"onload",
"onmousemove",
"onmousedown",
"ontouchstart",
"onclick",
"onscroll",
"onkeypress"
];
var timeoutId = null;
var finished = function () {
//You'd put your logic in here to clear the session.
//This gets fired when the user is deemed inactive.
console.log("User inactive.");
};
//Keep track of if the timer has finished, handy if the user stays on
//the page when this has finished, as you can compare it's value to
//detirmine if you should keep running the timeout.
var hasFinished = false;
var reset = function () {
if (hasFinished === true) {
return;
}
//Clear any already running timeout
clearTimeout(timeoutId);
//Start the new timeout
timeoutId = setTimeout(function () {
hasFinished = true;
if (typeof(finished) === "function") {
finished();
}
}, inactiveTime);
//If events reset the timer, apply them
//Attach events which will reset the timer
for (var i = 0, max = interactionEvents.length; i < max; i++) {
document[interactionEvents[i]] = reset();
}
};
//Start the timer with this
reset();
Something like that!

Audio Event Listener Getting Called Non Stop

I initially noticed this issue with the timeupdate event that was only occurring on firefox, but found that it applies to other event listeners as well (so far I have seen it on canplay)
Basically, I have an element that I create inside of an angular directive; I bind a function to ontimeupdate, and that event gets fired nonstop even when the currentTime value is 0
Brief summary of the code:
// angular directive controller function
...
DOMAudioObject = createAudioObject(src);
DOMAudioObject.audio.ontimeupdate = onTimeUpdate;
function createAudioObject(src) {
return {
audio: new Audio(src),
playback: $scope.playback,
name: $scope.name
};
}
function onTimeUpdate() {
var currentTime = DOMAudioObject.audio.currentTime;
console.log(currentTime);
$scope.currentTime = currentTime;
$scope.audio.currentTime = currentTime;
$scope.$apply();
}
Full controller code:
function audioCtrl($scope) {
// private reference to the DOM object that plays the audio
var DOMAudioObject,
watchers = {unbind: {}};
// register watchers once the model is available, we need at least the id field
watchers.unbind.model = $scope.$watch('model', init);
// remove watchers when the user navigates away
$scope.$on('$destroy', destroyWatchers);
function applyAudioPropertiesAsync() {
DOMAudioObject.audio.volume = $scope.volume;
DOMAudioObject.audio.currentTime = $scope.currentTime;
$scope.audio.duration = DOMAudioObject.audio.duration;
}
function applyAudioMetaProperties() {
$scope.audio = $scope.audio || {};
$scope.audio.id = $scope.model.id;
$scope.audio.playback = $scope.playback;
$scope.audio.name = $scope.model.name;
$scope.audio.volume = $scope.volume;
$scope.audio.currentTime = $scope.currentTime;
$scope.audio.duration = DOMAudioObject.audio.duration || 0;
}
// fired when the audio object has been loaded from src
function bindAudio(src, oldSrc) {
if (src === undefined) {
return;
}
// now safe to register watchers since they rely on the audio object
registerWatchers();
// if there is already a stored audio object associated with this visual, use it
DOMAudioObject = $audio.get($scope.model.id);
// audio src has been updated, reflect by pausing and creating a new audio object
if (oldSrc && src !== oldSrc) {
$scope.playback.play = false;
$scope.currentTime = 0.0;
pause(DOMAudioObject);
DOMAudioObject = null;
}
// create a new audio object or use stored values instead of reinitializing each time
if (!DOMAudioObject) {
DOMAudioObject = createAudioObject(src);
// set in $audio service for persistence across views and controllers
$audio.set($scope.model.id, DOMAudioObject);
} else {
$scope.playback = DOMAudioObject.playback || $scope.playback;
$scope.currentTime = DOMAudioObject.audio.currentTime || $scope.currentTime;
$scope.volume = DOMAudioObject.audio.volume || $scope.volume;
}
// only bind meta properties, binding actual Audio object causes problems in some browsers
applyAudioMetaProperties();
// add values that must be calculated after initial load
DOMAudioObject.audio.oncanplay = applyAudioPropertiesAsync;
// tell playback progress indicator to move on timeupdate event by firing a digest cycle
DOMAudioObject.audio.ontimeupdate = onTimeUpdate;
// tell animate directive to stop scrolling when the audio has ended
DOMAudioObject.audio.onended = onAudioEnded;
// tell parent this directive is ready when the audio has fully loaded
watchers.unbind.duration = $scope.$watch('audio.duration', function (val) {
if (val > 0) {
$scope.$emit('audio.ready', {id: $scope.model.id, audio: $scope.audio});
watchers.unbind.duration();
}
});
}
// create a dom audio object
function createAudioObject(src) {
return {
audio: new Audio(src),
playback: $scope.playback,
name: $scope.model.name
};
}
function destroyWatchers() {
if (watchers.unbind.audio) {
watchers.unbind.audio();
}
if (watchers.unbind.playback) {
watchers.unbind.playback();
}
if (watchers.unbind.progress) {
watchers.unbind.progress();
}
if (watchers.unbind.volume) {
watchers.unbind.volume();
}
}
function init(visual) {
if (visual === undefined) {
return;
}
// prevent updates to visual model from rebinding audio
watchers.unbind.model();
// when the audio-src is available and fully loaded, create audio objects
watchers.unbind.audio = $scope.$watch('audioSrc', bindAudio);
}
function onAudioEnded() {
// ensure playback variables are updated
$scope.$apply($scope.playback.play = false);
$scope.currentTime = 0;
}
// timeupdate event to update scope attribute with that of the Audio object
function onTimeUpdate() {
var currentTime = DOMAudioObject.audio.currentTime;
$scope.currentTime = currentTime;
$scope.audio.currentTime = currentTime;
$scope.$apply();
}
// pause the current track
function pause(audio) {
if (audio) {
audio.audio.pause();
}
}
// play the current track
function play(audio) {
if (audio) {
audio.audio.play();
}
}
function registerWatchers() {
// allow audio to be toggled on/off
watchers.unbind.playback = $scope.$watch('playback.play', togglePlay);
// allow volume changes
watchers.unbind.volume = $scope.$watch('volume', updateVolume);
// allow seeking of audio
watchers.unbind.progress = $scope.$watch('currentTime', seek);
// update the name variable on the audio object so it reflects in global scope
watchers.unbind.name = $scope.$watch('model.name', applyAudioMetaProperties);
}
// move audio position pointer to a new place
function seek(val) {
var threshold = 1,
curr = DOMAudioObject.audio.currentTime;
if ((val >= curr + threshold) || (val <= curr - threshold)) {
DOMAudioObject.audio.currentTime = val;
}
}
// toggle play/pause
function togglePlay(val) {
if (val) {
play(DOMAudioObject);
$audio.setGlobal($scope.audio);
} else {
pause(DOMAudioObject);
}
}
// allow the volume to be changed, scope reference updates automatically (pass by reference)
function updateVolume(val) {
if (val) {
DOMAudioObject.audio.volume = val;
}
}
}
Then, as you see in that image, the onTimeUpdate() function keeps getting called over and over again, even though the value of currentTime hasnt changed (it is 0 each and every time)
Again this only occurs on firefox. Chrome, safari, and even internet explorer behave nicely. I am running firefox 40.0.3 on Mac OS X 10.11 El Capitan, angular 1.4.6
Does anyone have some insight into what might be happening here, and any potential solutions to fix it?
it turns out, the culprit was this line here
DOMAudioObject.audio.currentTime = $scope.currentTime;
Apparently, setting this value causes the event listeners to fire indefinitely. I have no idea why. But I removed this line and everything works as expected.
I posted a new question to see if anyone has some insight to this strangeness
Setting currentTime attribute programatically on audio element causes event listeners to fire indefinitely

A pattern to queue notifications?

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.

clearInterval does not work when called recursivly [duplicate]

This question already has an answer here:
recursive clearInterval does not work
(1 answer)
Closed 8 years ago.
I have the following function in javaScript. This function is called when i detect a need to re-load the stylesheet. for example, doe to user language change, so the text won't fit the buttons anymore. The problem is, it gets stuck in the setInterval part. looping into it endlessly. I can see in the chrome debugger that it does get to the clearInterval part - but it wont clear. This function - resetStyle - is only called once.
Can anyone please help?
Thank you!
p.resetStyle = function () {
var that = this;
var oldStylesheet_href = $('#mainStylesheet').attr("href");
var i = oldStylesheet_href.indexOf('lang');
var lastLanguege = oldStylesheet_href.substring(i + 5, i + 7);
var prefix, sufix;
if (lastLanguege === createjs.mainManager.LangString) {
return;
}
prefix = oldStylesheet_href.substring(0, i - 1);
sufix = '&' + oldStylesheet_href.substring(i + 7, oldStylesheet_href.length);
var head = document.getElementsByTagName('head')[0]; // reference to document.head for appending/ removing link nodes
var link = document.createElement('link'); // create the link node
link.setAttribute('id', 'newStylesheet');
link.setAttribute('href', prefix + '&lang=' + createjs.mainManager.LangString + sufix);
link.setAttribute('rel', 'stylesheet');
link.setAttribute('type', 'text/css');
var sheet, cssRules;
// get the correct properties to check for depending on the browser
if ('sheet' in link) {
sheet = 'sheet';
cssRules = 'cssRules';
} else {
sheet = 'styleSheet';
cssRules = 'rules';
}
var timeout_id = setInterval(function () { // start checking whether the style sheet has successfully loaded
try {
if (link[sheet] && link[sheet][cssRules].length) { // SUCCESS! our style sheet has loaded
clearInterval(timeout_id); // clear the counters
clearTimeout(timeout_id);
that.onStyleReset();
}
} catch (e) {} finally {}
}, 10), // how often to check if the stylesheet is loaded
timeout_id = setTimeout(function () { // start counting down till fail
clearInterval(timeout_id); // clear the counters
clearTimeout(timeout_id);
head.removeChild(link); // since the style sheet didn't load, remove the link node from the DOM
that.onStyleReset();
}, 15000);
$('head').append(link);
$('#mainStylesheet').remove();
link.setAttribute('id', 'mainStylesheet');
};
You're reusing the variable timeout_id for two different things (your interval id and your timeout id), so you're overwriting your interval id when you call setTimeout. Change:
var timeout_id = setInterval(...
to:
var interval_id = setInterval(...
and update the variable where you call clearInterval as well: clearInterval(interval_id);

Need to delay javascript execution, but setTimeout is proving problematic

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);
}
}

Categories

Resources