Stop a javascript function - javascript

What I want:
Monitor a player to execute a function when it reach 85% of the movie - Ok
Execute a PHP script that insert some data into a Mysql table - Ok
Do this only one time (stop looping), since I want only one row in the Mysql table - Fail
My code:
jwplayer().onTime(function(evt) {
if (evt.position > ([evt.duration] * (85/100)) && x!=1) {
loadXMLDoc();
var x = 1;
}
});
Thanks

The problem is that x gets reset everytime
jwplayer().onTime(
(function () {
var check=true;
return function(evt) {
if (check && evt.position > ([evt.duration] * (85/100))) {
loadXMLDoc();
check=false;
}
}
})()
);

If you want the function to run only once with each page load, another approach is to make a function that commits suicide.
(function (player) {
var checkAndLoad = function(evt) {
if (evt.position > (evt.duration * (85/100))) {
loadXMLDoc();
checkAndLoad=function(evt){};
}
};
player.onTime(function(evt) {checkAndLoad(evt);});
})(jwplayer());
You need the extra indirection provided by the anonymous wrapper since onTime gets its own copy of the event listener, so overwriting checkAndLoad won't affect the registered listener directly.
If you want the listener to run more than once, register additional listeners that restore checkAndLoad at the appropriate events (e.g. the user seeks back to near the beginning).
(function (player) {
var timeListener;
function checkAndLoad(evt) {
if (evt.position > (evt.duration * (85/100))) {
loadXMLDoc();
timeListener=function(evt){};
}
}
timeListener = checkAndLoad;
player.onTime(function(evt) {timeListener(evt);});
player.onSeek(function(evt) {
if (evt.position < (evt.duration * (15/100))) {
timeListener=checkAndLoad;
}
});
player.onComplete(function (evt) {timeListener=checkAndLoad;});
})(jwplayer());
Better would be to unregister the listener, but the JW Player API doesn't currently expose the removeEventListener method.

Related

The event .click fires multiple times

On the page there is a link with id get-more-posts, by clicking on which articles are loaded. Initially, it is outside the screen. The task is to scroll the screen to this link by clicking on it. The code below does what you need. But the event is called many times. Only need one click when I get to this element scrolling.
p.s. sorry for my bad english
$(window).on("scroll", function() {
if((($(window).scrollTop()+$(window).height())+250)>=$(document).height()){
$('#get-more-posts').click();
}
});
Try use removeEventListener or use variable with flag, just event scroll detached more at once
You can set up throttling by checking if you are already running the callback. One way is with a setTimeout function, like below:
var throttled = null;
$(window).on("scroll", function() {
if(!throttled){
throttled = setTimeout(function(){
if((($(window).scrollTop()+$(window).height())+250)>=$(document).height()){
$('#get-more-posts').click();
throttled = null;
}
}.bind(window), 50);
}
}.bind(window));
Here's an ES6 version that might resolve the scoping issues I mentioned:
let throttled = null;
$(window).on("scroll", () => {
if(!throttled){
throttled = setTimeout(() => {
if((($(window).scrollTop()+$(window).height())+250)>=$(document).height()){
$('#get-more-posts').click();
throttled = null;
}
}, 50);
}
});
The last argument of setTimeout is the delay before running. I chose 50 arbitrarily but you can experiment to see what works best.
I don't know how true it is, but it works. After the event (click), delete the element id, and then add it again, so the click is performed once. Scroll the page to the desired item, click again, delete the id and add it again. It works. Can someone come in handy.
window.addEventListener('scroll', throttle(callback, 50));
function throttle(fn, wait) {
var time = Date.now();
return function() {
if ((time + wait - Date.now()) < 0) {
fn();
time = Date.now();
}
}
}
function callback() {
var target = document.getElementById('get-more-posts');
if((($(window).scrollTop()+$(window).height())+650)>=$(document).height()){
$('#get-more-posts').click();
$("#get-more-posts").removeAttr("id");
//$(".get-more-posts").attr("id='get-more-posts'");
};
}
window.removeEventListener('scroll', throttle(callback, 50));

Clearing setInterval() Issue

I have a setInterval on a function X that runs every 500ms. In this function X, I call another function Y that essentially binds an event on some divs. However, I would like to unbind these events the next time the function X is called (to start "fresh"). My code doesn't seem to work:
setInterval(this.board.updateBoard, 500); //called from another constructor
This then initiates the functions below:
Board.prototype.updateBoard = function() {
//I attempt to unbind ALL my divs
var divs = this.$el.find("div");
for(var i = 0; i < divs.length; i++) {
$(divs[i]).unbind(); //Apparently this doesn't work?
}
//...some code here...
//find appropriate $div's (multiple of them), and then calls this.beginWalking() below on each of those
//loop here
this.beginWalking($div, direction + "0", direction + "1");
//end of loop
}
//alternate between classes to give appearance of walking
Board.prototype.beginWalking = function ($div, dir0, dir1) {
return setInterval(function () {
if ($div.hasClass(dir0)) {
$div.removeClass(dir0);
$div.addClass(dir1);
} else {
$div.removeClass(dir1);
$div.addClass(dir0);
}
}.bind(this), 80);
};
Basically, updateBoard is called every 500ms. Each time it's called, beginWalking is called to set another interval on a div. The purpose of this other interval, which functions correctly, is to add and remove a class every 80ms. I just can't seem to unbind everything before the next updateBoard is called.
Any suggestions appreciated!
use clearInterval()
edit: $(selector).toggleClass(dir0) might also be helpful
// In other file, use a global (no var) if you need to read it from another file:
updaterGlobal = setInterval(this.board.updateBoard, 500);
// store interval references for clearing:
var updaterLocals = [];
Board.prototype.updateBoard = function() {
//I attempt to unbind ALL my divs
var divs = this.$el.find("div");
// Stop existing div timers:
while(updaterLocals.length > 0){
clearInterval(updaterLocals[0]);
updaterLocals.shift(); // remove the first timer
}
//...some code here...
//loop here to call the below on several $div's
this.beginWalking($div, direction + "0", direction + "1");
//end of loop
}
//alternate between classes to give appearance of walking
Board.prototype.beginWalking = function ($div, dir0, dir1) {
var interval = setInterval(function () {
if ($div.hasClass(dir0)) {
$div.removeClass(dir0);
$div.addClass(dir1);
} else {
$div.removeClass(dir1);
$div.addClass(dir0);
}
}.bind(this), 80);
// Save the timer:
updaterLocals.push(interval);
return;
};

Hide background-image inline styling from source code

I needed to create a simple memory game for a course I'm attending and I came up with this:
Simple memory game
$(document).ready(function(){
// Set initial time to 0
var currentTime = 0;
// Set initial score to 0
var score = 0;
// Set initial attempt to 0
var attempts = 0;
// Placeholder
var timeHolder;
// Start the game when clicking the button #start
$('#start').on('click', init);
// Check if user hasn't clicked #start button yet
$('.board_cell').on('click.initialCheck', checkInitial);
function init() {
// Shuffle elements at beginning
shuffle();
// Handle click event and check if elements match
$('.board_cell').on('click', checkAccuracy).off('click.initialCheck');
$('#start').off('click', init);
$('#reset').on('click', reset);
// stop timer
clearInterval(timeHolder)
// Timer function will be called every 100ms
timeHolder = window.setInterval(timer, 100);
}
/*
* Main function to handle click events and do actions
* #checkAccuracy
*/
function checkAccuracy() {
var self = $(this),
allElements = $('.board_cell'),
// Url of background-image
bgUrl = self.children('.back').css('background-image'),
// Get relevant part (the image name) from the url
elementType = bgUrl.slice(bgUrl.lastIndexOf('/') + 1, bgUrl.lastIndexOf('.'));
// Flip the clicked element
self.addClass('flipped ' + elementType);
// Check if clicked element is already visible
if (self.hasClass('success')) {
showMessage('Das ist schon aufgedeckt ;)');
// Abort function by returning false
return false;
}
if($('.flipped').length >= 2) {
// Prevent clicking on other elements when 2 elements are already visible
allElements.off('click');
// Increase attempts
attempts++;
setAttempts();
}
if($('.'+elementType).length == 2) {
// If the same are visible
score++;
setScore();
showMessage(['That was a right one!!!', 'Now you got it!', 'Terrific!']);
toggleClasses('success', function(){
// Callback when toggleClasses has finsihed
if($('.success').length == allElements.length){
// If alle elements are visible
showMessage('Everything exposed, congrats!');
$('#overlay').fadeIn();
// Kill interval to prevent further increasing
clearInterval(timeHolder);
}
});
} else {
// If they are not the same
if($('.flipped').length == 2) {
toggleClasses();
showMessage(['Uhh that was wrong...', 'Are you drunk?', 'Try it again...', 'You better stop playing!',
'Seems like you need to train much more...', 'Annoying?', 'C\'mon!']);
}
}
}
/*
* Function to reset the game
*/
function reset() {
// turn elements and prevent clicking
$('.board_cell').removeClass('success flipped').off('click');
// hide overlay if visible
if($('#overlay').is(':visible')) {
$('#overlay').fadeOut();
}
// stop timer
clearInterval(timeHolder)
// set time to 0
currentTime = 0;
// set attempts to 0
attempts = 0;
setAttempts();
// set score to 0
score = 0;
setScore();
// hide message
showMessage('');
// set visible time to 0
$('#time span').text(currentTime);
// Check if user has clicked #start button
$('.board_cell').on('click.initialCheck', checkInitial);
$('#start').on('click', init);
}
/*
* Function to show a message
*/
function showMessage(msg) {
if(typeof msg == 'object') {
var randomNumber = Math.floor((Math.random()*msg.length)+1);
msg = msg[randomNumber-1];
}
$('#message span').html(msg);
}
/*
* Function to toggleClasses on clickable elements
*/
function toggleClasses(extraClass, callback) {
setTimeout(function(){
$('.flipped').removeClass().addClass('board_cell '+extraClass);
$('.board_cell').on('click', checkAccuracy);
if(typeof callback != 'undefined') {
callback();
}
}, 1000);
}
/*
* Function to increase score
*/
function setScore() {
$('#score span').text(score);
}
/*
* Function to increase attempts
*/
function setAttempts() {
$('#attempts span').text(attempts);
}
/*
* Function for timer
*/
function timer() {
currentTime = currentTime + 1;
$('#time span').text(currentTime/10);
}
/*
* Function for showing message when user hasn't clicked #start button yet
*/
function checkInitial() {
showMessage('You need to press the "Start Game" button to beginn');
}
/*
* Function for shuffling elements
*/
function shuffle() {
var elementsArray = [];
$('.back').each(function(index){
elementsArray.push($(this).css('background-image'))
});
elementsArray.sort(function() {return 0.5 - Math.random()})
$('.back').each(function(index){
$(this).css('background-image', elementsArray[index]);
});
}
});
The hidden images are added through inline styling with background-image and are shuffled on each start/reset but that doesn't prevent a user to cheat during the game (looking at the source code tells the solution immediately).
I'd like to know if there is a way to hide the background-image value in the source code?
I tried some base64 encryption but as the browser interprets this immediately it doesn't serve at all. Another idea I had was to encrypt the background-image with php, display a random output (which was saved in a database for instance) in the markup and communicate through ajax requests with the php file on each click event. I'm not sure if this works, but anyway it seems like a circuitous way of handling it..
Any suggestion on how to solve this in safe and efficient way?
A safe (and overkill) solution would be to do this entirely serverside. Start a new game by generating a random session id and shuffling the squares. Then, your clientside JS requests the square images through AJAX via some sort of API:
GET /<session_id>/square/<x>/<y>
Once you rate limit the requests serverside to prevent a user from downloading all of the images at once, your game is safe.

Jquery bind not working while within a javascript recursive loop

I am writing a piece of code that changes some lights on a screen from red to green randomly and waits for the user to hit the key that corresponds to the light lit.
When I run this code you are able to hit the a,d,j or l key and an alert will pop up. However, as soon as I click the start button no keys are recognised. And when the loop has finished the bind still seems to become disabled. I have tried moving the bind to other places but I have had no joy. Your help is much appreciated.
$( function() {
$('#start').bind('click', function() { main(); });
$(document).bind('keypress', function(e) { keyPress(e); } );
} );
function getRand(val) {
return Math.floor(Math.random()*val)+1;
}
function main() {
preD = new Date;
preDs = preD.getTime();
randTime=Math.floor(Math.random()*1001)+1500;
playSound();
flash();
}
function flash() {
zone = getZone();
setTimeout(function() {
$('#r'+zone).css("background-image", "url(images/rea_grn.jpg)");
setTimeout(function() {
$('#r'+zone).css("background-image", "url(images/rea_red.jpg)");
if(cond[1] < 8) {
main();
}
} , 200);
} , randTime);
}
function getZone() {
if(condition==1) {
zone = getRand(2);
if( test[1][zone] < 8 ) {
test[1][zone] += 1;
cond[1] += 1;
return zone;
} else {
getZone();
}
}
}
function keyPress(e) {
var evtobj=window.event? event : e //distinguish between IE's explicit event object (window.event) and Firefox's implicit.
var unicode=evtobj.charCode? evtobj.charCode : evtobj.keyCode
var actualkey=String.fromCharCode(unicode)
if (actualkey=="a" || actualkey=="d" || actualkey=="j" || actualkey=="l" ) {
dd = new Date;
reat = dd.getTime();
alert(1);
//keypressed[condition][zone]['k']=actualkey;
//keypressed[condition][zone]['t']=(reat-preDs);
}
}
The reason that this could be happening is, when you generate code dynamically or alter any existing code the bind needs to be done again, because the function to bind just runs once and only for the members already created. So when you create dynamically code, you are forced to run the binding function to recognize the new elements.
this ways is not very recommended, instead of this, you could bind a container like 'div' or something and inside of this validate which element is calling you. This will work because your container is created once and the binding is properly assigned and doesn't matter if the content of your container changes, the binding always work.
Regards
Using a jquery sound plugin was the answer.
Fixed it with this : plugins.jquery.com/project/sound_plugin

setTimeout / clearTimeout problems

I try to make a page to go to the startpage after eg. 10sec of inactivity (user not clicking anywhere). I use jQuery for the rest but the set/clear in my test function are pure javascript.
In my frustation I ended up with something like this function that I hoped I could call on any click on the page. The timer starts fine, but is not reset on a click. If the function is called 5 times within the first 10 seconds, then 5 alerts will apear... no clearTimeout...
function endAndStartTimer() {
window.clearTimeout(timer);
var timer;
//var millisecBeforeRedirect = 10000;
timer = window.setTimeout(function(){alert('Hello!');},10000);
}
Any one got some lines of code that will do the trick?
- on any click stop, reset and start the timer.
- When timer hits eg. 10sec do something.
You need to declare timer outside the function. Otherwise, you get a brand new variable on each function invocation.
var timer;
function endAndStartTimer() {
window.clearTimeout(timer);
//var millisecBeforeRedirect = 10000;
timer = window.setTimeout(function(){alert('Hello!');},10000);
}
The problem is that the timer variable is local, and its value is lost after each function call.
You need to persist it, you can put it outside the function, or if you don't want to expose the variable as global, you can store it in a closure, e.g.:
var endAndStartTimer = (function () {
var timer; // variable persisted here
return function () {
window.clearTimeout(timer);
//var millisecBeforeRedirect = 10000;
timer = window.setTimeout(function(){alert('Hello!');},10000);
};
})();
That's because timer is a local variable to your function.
Try creating it outside of the function.
A way to use this in react:
class Timeout extends Component {
constructor(props){
super(props)
this.state = {
timeout: null
}
}
userTimeout(){
const { timeout } = this.state;
clearTimeout(timeout);
this.setState({
timeout: setTimeout(() => {this.callAPI()}, 250)
})
}
}
Helpful if you'd like to only call an API after the user has stopped typing for instance. The userTimeout function could be bound via onKeyUp to an input.
Not sure if this violates some good practice coding rule but I usually come out with this one:
if(typeof __t == 'undefined')
__t = 0;
clearTimeout(__t);
__t = setTimeout(callback, 1000);
This prevent the need to declare the timer out of the function.
EDIT: this also don't declare a new variable at each invocation, but always recycle the same.
Hope this helps.
Practical example Using Jquery for a dropdown menu !
On mouse over on #IconLoggedinUxExternal shows div#ExternalMenuLogin and set time out to hide the div#ExternalMenuLogin
On mouse over on div#ExternalMenuLogin it cancels the timeout.
On mouse out on div#ExternalMenuLogin it sets the timeout.
The point here is always to invoke clearTimeout before set the timeout, as so, avoiding double calls
var ExternalMenuLoginTO;
$('#IconLoggedinUxExternal').on('mouseover mouseenter', function () {
clearTimeout( ExternalMenuLoginTO )
$("#ExternalMenuLogin").show()
});
$('#IconLoggedinUxExternal').on('mouseleave mouseout', function () {
clearTimeout( ExternalMenuLoginTO )
ExternalMenuLoginTO = setTimeout(
function () {
$("#ExternalMenuLogin").hide()
}
,1000
);
$("#ExternalMenuLogin").show()
});
$('#ExternalMenuLogin').on('mouseover mouseenter', function () {
clearTimeout( ExternalMenuLoginTO )
});
$('#ExternalMenuLogin').on('mouseleave mouseout', function () {
clearTimeout( ExternalMenuLoginTO )
ExternalMenuLoginTO = setTimeout(
function () {
$("#ExternalMenuLogin").hide()
}
,500
);
});
This works well. It's a manager I've made to handle hold events. Has events for hold, and for when you let go.
function onUserHold(element, func, hold, clearfunc) {
//var holdTime = 0;
var holdTimeout;
element.addEventListener('mousedown', function(e) {
holdTimeout = setTimeout(function() {
func();
clearTimeout(holdTimeout);
holdTime = 0;
}, hold);
//alert('UU');
});
element.addEventListener('mouseup', clearTime);
element.addEventListener('mouseout', clearTime);
function clearTime() {
clearTimeout(holdTimeout);
holdTime = 0;
if(clearfunc) {
clearfunc();
}
}
}
The element parameter is the one which you hold. The func parameter fires when it holds for a number of milliseconds specified by the parameter hold. The clearfunc param is optional and if it is given, it will get fired if the user lets go or leaves the element. You can also do some work-arounds to get the features you want. Enjoy! :)
<!DOCTYPE html>
<html>
<body>
<h2>EJEMPLO CONOMETRO CANCELABLE</h2>
<button onclick="inicioStart()">INICIO</button>
<input type="text" id="demostracion">
<button onclick="finStop()">FIN</button>
<script>
let cuenta = 0;
let temporalTiempo;
let statusTime = false;
function cronometro() {
document.getElementById("demostracion").value = cuenta;
cuenta++;
temporalTiempo = setTimeout(cronometro, 500);
}
function inicioStart() {
if (!Boolean(statusTime)) {
statusTime = true;
cronometro();
}
}
function finStop() {
clearTimeout(temporalTiempo);
statusTime = false;
}
</script>
</body>
</html>

Categories

Resources