I use a JavaScript function to change the page when a function is clicked. An ajax call is made inside the function.
As the browser is slow. If I click more than one time on the button before the page changes (AJax div load. Page actually not changed), an ajax call is made for each click.
I used the following method to prevent. But even it is called many times. How can I prevent it?
var isClicked = function() {}
isClicked.init = function() {
this.clicked = false;
}
function myAjaxFunction {
//some statements
if(isClicked.clicked == 'undefined')
isClicked.clicked = false;
if(isClicked.clicked)
return false;
isClicked.clicked = true;
// my ajax call here
isClicked.clicked = false;
//some statements
}
var isClicked = false;
function AjaxFunction(){
if(isClicked){
return false;
}
// Set your variable.
isClicked = true;
// Do yourAjaxCall and use isClicked = false in the callback of that function.
}
Related
I'm using third party jQuery tabs plugin to navigate my web application. Every tab below has "next" button. When the next button is click, it will trigger the function onNext. From there, I can validate my fields before moving to the second tab. The validation works well, if I tried to validate all the fields. But when I validate through API calls and return the data. It doesn't work. I had been used $scope.$apply() and setTimeOut. Both didn't work.
Controller without service (Working Example)
jQuery('.js-wizard-classic-validation').bootstrapWizard({
'tabClass': '',
'previousSelector': '.wizard-prev',
'nextSelector': '.wizard-next',
'onTabShow': function(tab, nav, index) { }
'onNext': function(tab, navigation, index) {
if(index == 1){
// This is when the 'next' button is click on the first tab
var full_location_validated = false;
// All the if else statements goes here to validate the fields
$scope.$apply(); // Without this, next button will does nothing never update
return full_location_validated;
}
}
Controller with service (Doesn't Work)
jQuery('.js-wizard-classic-validation').bootstrapWizard({
'tabClass': '',
'previousSelector': '.wizard-prev',
'nextSelector': '.wizard-next',
'onTabShow': function(tab, nav, index) { }
'onNext': function(tab, navigation, index) {
if(index == 1){
// This is when the 'next' button is click on the first tab
var full_location_validated = false;
// Assume service return (success == true)
myService.ValidateLocation(function(success){
if(success == true)
full_location_validated = true;
else
full_location_validated = false;
});
/* CASE 1 */
// $scope.$apply();
// return full_location_validated; // it returns false
/* CASE 2 */
setTimeout(function(){
$scope.$apply();
return full_location_validated; // it returns true
},1500);
}
}
Problem
When the next button is clicked, nothing happens because full_location_validated returns false instead of true with service.
I see in your code following issues:
1) Avoid to use setTimeout
If you write Angular application, don't use setTimeout, use $timeout instead. Angular doesn't know about setTimeout delay and you need to use $scope.$apply();.
This code snippet:
setTimeout(function(){
$scope.$apply();
return full_location_validated; // it returns true
},1500);
You can replace with:
$timeout(function (){
return full_location_validated;
}, 1500);
2) myService.ValidateLocation its a Async call:
myService.ValidateLocation(function(success){
if(success == true)
full_location_validated = true;
else
full_location_validated = false;
});
// ...
return full_location_validated;
You first return full_location_validated as false and only after some delay (in your case 1500 ms) you full_location_validated turned to be a true.
To make it work I would try to use Promises or get rid of hard coded value 1500ms like:
var releaseTimeout = false;
myService.ValidateLocation(function(success){
if(success == true)
full_location_validated = true;
else
full_location_validated = false;
releaseTimeout = true;
});
var waitForValidate = function () {
$timeout(function () {
if(!releaseTimeout){
return waitForValidate();
}
else{
return full_location_validated;
}
}, 100);
};
return waitForValidate();
**Didn't test it
3) DOM manipulations and using 3d party
Its not a good practice to do DOM manipulations and using 3d party like jQuery, in controllers. Create directive for that purpose.
I am trying to make a simple button to turn on and off my audio file.
Once I click play I can't turn it off.
I'm using an image as my button and div.onclick to spark the function.
The html audio tag has id audioPlay.
I use the global boolean playSound, initially set to false.
My script looks like this:
var playSound = false;
if (playSound != true) {
audioButton.src = '../../testStuff/audioPlay.png';
audio.onclick = function () {
document.getElementById('audioPlay').play();
playSound = true;
console.log(playSound);
}
}
if (playSound == true) {
audioButton.src = '../../testStuff/audioStop.png';
audio.onclick = function () {
document.getElementById('audioPlay').pause();
playSound = false;
}
}
When I click on it the first time it works fine. It sets playSound to true.
However, when I go to click it a second time, nothing happens. Something is setting playSound back to false and I don't know why.
I've tried switching the ==true if statement above the false one, as well as rolling both if statements into a single onclick function but it still operates this way.
Any ideas?
I think the issue is that you're adding two separate click handlers that are both being executed on each click. This results in playSound always being set to true, but then immediately being set back to false.
Instead, write a function called togglePlay that does something like the following, and set that as your click handler, but only once.
function togglePlay () {
if (playSound) {
audioButton.src = '../../testStuff/audioStop.png';
document.getElementById('audioPlay').pause();
playSound = false;
} else {
audioButton.src = '../../testStuff/audioPlay.png';
document.getElementById('audioPlay').play();
playSound = true;
}
}
//play is the name of the botton that u click to play the music and psuse
const isplay;
play.addEventListener('click',()=>{
const playmusic = ()=>{
isplay = true;
console.log('play music');
music.play();
};
const pausemusic = ()=>{
isplay = false;
music.pause();
};
if(isplay){
pausemusic();
}else{
playmusic();
};
)};
I have a refresh function:
function refresh(nRefresh)
{
TimerSetting = document.all.curRefresh.Timer;
document.all.curRefresh.Timer = 'On';
nTimeOut = nRefresh;
updateKnlButtons();
psStatusUpdate();
}
This function reload the page.
After clicking on a button I give refresh(5) to refresh a page after 5 seconds. Due to some reason I want to fire a function after refresh function is completed, but this function is getting fired before refresh function is completed. How to make sure disable function is called after refresh function is completed?
function disableButton()
{
idStopSelBtn.style.cursor='wait';
idStartSelBtn.style.cursor='wait';
idBounceRunningBtn.style.cursor='wait';
idStopAllBtn.style.cursor='wait';
idStartAllBtn.style.cursor='wait';
idBounceSelBtn.style.cursor='wait'
idStopSelBtn.src='images/Button/Disabled/Stop-Selected.gif';
idStartSelBtn.src='images/Button/Disabled/Start-Selected.gif';
idBounceRunningBtn.src='images/Button/Disabled/Bounce-Running.gif';
idStopAllBtn.src='images/Button/Disabled/Stop-All.gif';
idBounceSelBtn.src='images/Button/Disabled/Bounce-Selected.gif'
idStartAllBtn.src='images/Button/Disabled/Start-All.gif';
idStopSelBtn.onclick="return false";
}
You may just add another parameter (disable flag) to achieve the desired result.
function refresh(nRefresh, disable) {
TimerSetting = document.all.curRefresh.Timer;
document.all.curRefresh.Timer = 'On';
nTimeOut = nRefresh;
updateKnlButtons();
psStatusUpdate();
if (disable) disableButton();
}
Are you looking for
function refresh(nRefresh, disableButton) {
TimerSetting = document.all.curRefresh.Timer;
document.all.curRefresh.Timer = 'On';
nTimeOut = nRefresh;
updateKnlButtons();
psStatusUpdate();
disableButton();
}
function disableButton() {
idStopSelBtn.style.cursor='wait';
idStartSelBtn.style.cursor='wait';
idBounceRunningBtn.style.cursor='wait';
idStopAllBtn.style.cursor='wait';
idStartAllBtn.style.cursor='wait';
idBounceSelBtn.style.cursor='wait'
idStopSelBtn.src='images/Button/Disabled/Stop-Selected.gif';
idStartSelBtn.src='images/Button/Disabled/Start-Selected.gif';
idBounceRunningBtn.src='images/Button/Disabled/Bounce-Running.gif';
idStopAllBtn.src='images/Button/Disabled/Stop-All.gif';
idBounceSelBtn.src='images/Button/Disabled/Bounce-Selected.gif'
idStartAllBtn.src='images/Button/Disabled/Start-All.gif';
idStopSelBtn.onclick="return false";
}
refresh(5, disableButton);
For some reason when I call generateTabs I get this.divRow1 is undefined.
However when I instantiate Criteria it is not undefined.
What am I doing wrong?
function Criteria() {
this.divPortfolio = $("#portfolio_div");
this.divImport = $("#lstimprt_div");
this.rolling = $('#rolling');
this.datePickers = $("#datepickers");
this.dateStart = $("#datepicker");
this.dateEnd = $("#datepicker2");
this.btnToggle = $("#toggle");
this.btnRun = $("#run_report");
this.divRow1 = $("#Row1");
this.dateChoice = $("#datechoice");
this.minDate = $(".minDate");
}
Criteria.prototype.generateToggle = function () {
/** Toggle button toggle elements chosen open/closed **/
button.click(function () {
this.divRow1.toggle("slow");
this.divImport.toggle("slow");
$("#mindate_div").toggle("slow");
$("#header_row").toggle("slow");
return false; //Stops postback
});
};
/** When page loads **/
$(document).ready(function () {
var controls = new Criteria();
var mindate = controls.minDate.html();
controls.onloadHide();
controls.generatePicker(controls.dateStart);
controls.generatePicker(controls.dateEnd);
controls.generateToggle(controls.btnToggle);
});
it is because of the execution context of the callback method is not the Criteria object, it is the clicked button.
One solution to this is to pass a custom execution context using $.proxy()
button.click($.proxy(function () {
this.divRow1.toggle("slow");
this.divImport.toggle("slow");
$("#mindate_div").toggle("slow");
$("#header_row").toggle("slow");
return false; //Stops postback
}, this));
Another solution is to use a closure variable
var that = this;
button.click(function () {
that.divRow1.toggle("slow");
that.divImport.toggle("slow");
$("#mindate_div").toggle("slow");
$("#header_row").toggle("slow");
return false; //Stops postback
});
button.click(function () {
this.divRow1.toggle("slow");
this.divImport.toggle("slow");
$("#mindate_div").toggle("slow");
$("#header_row").toggle("slow");
return false; //Stops postback
});
this has a different scope within the click event than outside of it. Specifically, jQuery will bind this to the item the event is being fired on (the button in this case). If you set a breakpoint and examine this in the dev console at this point you'll see it refers to a DOM element.
You can get around this by setting a self variable like this, and referencing that instead.
Criteria.prototype.generateToggle = function () {
var self = this;
/** Toggle button toggle elements chosen open/closed **/
button.click(function () {
self.divRow1.toggle("slow");
self.divImport.toggle("slow");
$("#mindate_div").toggle("slow");
$("#header_row").toggle("slow");
return false; //Stops postback
});
};
Criteria.prototype.generateToggle = function () {
var that = this;
/** Toggle button toggle elements chosen open/closed **/
button.click(function () {
that.divRow1.toggle("slow");
that.divImport.toggle("slow");
$("#mindate_div").toggle("slow");
$("#header_row").toggle("slow");
return false; //Stops postback
});
};
The "this" context has changed inside the button click. Try the above
I've written my first bit of proper jQuery for an image slideshow, that allows users to scroll up and down through some images:
$(window).load(function(){
$('.scrollUp').click(function(){
$('.cardWrapper:visible:first').prevAll(':hidden:first').slideDown(function(){
$('.cardWrapper:visible:last').slideUp();
});
return false;
});
$('.scrollDown').click(function(){
if($('.cardWrapper:last').is(':hidden')){
$('.cardWrapper:visible:last').nextAll(':hidden:first').slideDown();
$('.cardWrapper:visible:first').slideUp();
}
else{
$('.cardWrapper:last').after('<div class="cardWrapper"></div>');
$('.cardWrapper:last').load('/followedTestSingle/?sequence={{gr.sequence_token}}', function(){
$('.cardWrapper:visible:first').slideUp();
});
}
return false;
});
});
The problem I have is that if you click very fast on the .scrollDown element link - it loses all the content as it hasn't had the time to add the extra ( i think) - and thus it starts to fail.
Is there a way to make jQuery not accept any new click on an element until its run all of this function?
Maybe something like
var scrollDownClickActive = false;
$('.scrollDown').click(function(){
if (scrollDownClickActive) return false;
scrollDownClickActive = true;
if($('.cardWrapper:last').is(':hidden')){
$('.cardWrapper:visible:last').nextAll(':hidden:first').slideDown();
$('.cardWrapper:visible:first').slideUp(200, function(){ scrollDownClickActive = false; } );
}
else
{
$('.cardWrapper:last').after('<div class="cardWrapper"></div>');
$('.cardWrapper:last').load('/followedTestSingle/?sequence={{gr.sequence_token}}', function(){
$('.cardWrapper:visible:first').slideUp(200, function(){ scrollDownClickActive = false; } );
});
}
return false;
});
Using a flag to determine if the function is active or not.
The use of binding and unbinding removes the use of flag variables =)
function scroller(obj){
$(obj).unbind('click');
if($('.cardWrapper:last').is(':hidden')){
$('.cardWrapper:visible:last').nextAll(':hidden:first').slideDown();
$('.cardWrapper:visible:first').slideUp();
scrollDownClickActive = false;
}
else
{
$('.cardWrapper:last').after('<div class="cardWrapper"></div>');
$('.cardWrapper:last').load('/followedTestSingle/?sequence={{gr.sequence_token}}', function(){
$('.cardWrapper:visible:first').slideUp();
scrollDownClickActive = false;
});
}
$(obj).click(function(){scroller(this);});
}
$('.scrollDown').click(function(){
scroller(this);
});
Hope this helps!
If it's clicking an button element, just have your function disable it and re-enable it in the completion callback function.
Otherwise just write your function to check for a variable value which prevents it from running. If the variable isn't set, have it set a the value (something like var busy = true;) in the handler and set it back to false in the completion callback.
You can use a flag to indicate that it is scrolling (as suggested by MiffTheFox), but you'll have to unset the flag in the slide callback because the slide happens asynchronously:
$(function(){
var scrolling = false;
function startScrolling() {
if(scrolling) return false;
return scrolling = true;
}
function scrollComplete() {
scrolling = false;
}
$('.scrollUp').click(function() {
if(startScrolling()) return false;
$('.cardWrapper:visible:first')
.prevAll(':hidden:first').slideDown(function() {
$('.cardWrapper:visible:last').slideUp(scrollComplete);
});
return false;
});
$('.scrollDown').click(function() {
if(startScrolling()) return false;
if($('.cardWrapper:last').is(':hidden')) {
$('.cardWrapper:visible:last').nextAll(':hidden:first').slideDown();
$('.cardWrapper:visible:first').slideUp(scrollComplete);
} else {
$('.cardWrapper:last').after('<div class="cardWrapper"></div>');
$('.cardWrapper:last').load('/followedTestSingle/?sequence={{gr.sequence_token}}', function() {
$('.cardWrapper:visible:first').slideUp(scrollComplete);
});
}
return false;
});
});
Disclaimer: I haven't checked your code to see how valid it is, I've just added the flag and the callbacks for you.