prevent function from running twice - javascript

I have a slideshow which runs automatically and you can skip to an image by clicking on a button.
It works fine if you click one of the buttons when the image is static, but if you click while the fade functions are running it will run the functions twice which creates some kind of loop which eventually grinds the browser to a stand still!
I know I need to add some kind of "isRunning" flag, but I don't know where.
Here's a link to a jsfiddle - http://jsfiddle.net/N6F55/8/
And code also below...
javascript:
$(document).ready(function() {
var images=new Array();
var locationToRevealCount=6;
var nextimage=2;
var t;
var doubleclick;
addIcons();
function addIcons() {
while (locationToRevealCount>0) {
$("#extraImageButtons").append('<img class="galleryButtons" src="http://www.steveszone.co.uk/images/button_sets/pink_square_button1n.png" alt="'+locationToRevealCount+'" />');
images[locationToRevealCount]='http://www.tombrennand.net/'+locationToRevealCount+'a.jpg';
locationToRevealCount--;
};
$('.homeLeadContent').prepend('<img class="backgroundImage" src="http://www.tombrennand.net/1a.jpg" />');
$("#extraImageButtons img.galleryButtons[alt*='1']").attr("src","http://www.steveszone.co.uk/images/button_sets/black_square_button1n.png");
runSlides();
}
function runSlides() {
clearTimeout(t);
t = setTimeout(doSlideshow,3000);
}
function doSlideshow() {
if($('.backgroundImage').length!=0)
$('.backgroundImage').fadeOut(500,function() {
$('.backgroundImage').remove();
slideshowFadeIn();
});
else
slideshowFadeIn();
}
function slideshowFadeIn() {
if(nextimage>=images.length)
nextimage=1;
$("#extraImageButtons img.galleryButtons").attr("src","http://www.steveszone.co.uk/images/button_sets/pink_square_button1n.png");
$("#extraImageButtons img.galleryButtons[alt*='"+nextimage+"']").attr("src","http://www.steveszone.co.uk/images/button_sets/black_square_button1n.png");
$('.homeLeadContent').prepend($('<img class="backgroundImage" src="'+images[nextimage]+'" style="display:none;">').fadeIn(500,function() {
nextimage++;
runSlides();
}));
}
$("#extraImageButtons img.galleryButtons").live('click', function() {
nextimage=$(this).attr("alt");
$("#extraImageButtons img.galleryButtons").attr("src","http://www.steveszone.co.uk/images/button_sets/pink_square_button1n.png");
$(this).attr("src","http://www.steveszone.co.uk/images/button_sets/black_square_button1n.png");
clearTimeout(t);
doSlideshow();
});
});
html:
<div class="homeLeadContent" style="width:965px;">
</div>
<div id="extraImageButtons"></div>

Two changes make it work better for me:
Down in the "extra image buttons" handler, you call "clearInterval()" but that should be changed to "clearTimeout()".
I added another call to "clearTimeout(t)" in the "runSlides()" function right before it sets up another timeout.
Clicking on the big "CLICK ME" button might still do weird things.
edit — well here is my fork of the original jsfiddle and I think it's doing the right thing. In addition to calling "clearTimeout()" properly, I also changed the code in "doSlideshow()" so that it empties out the content <div> before adding another image.

Related

Problems with automating the function jquery

Hello guys Here i have a function that reacts two time by a same button. One time when it's clicked it fades out other time it fades in, but the problem is one that after 2 clicks it stops responding. I am trying to make it loop but. I don't have any clue. I tried with the clickCounts ++ and if statements but it didn't give me any fruit.
so if you guys have any idea I'm quite opened to any suggestions.
$(function() {
$('#two').one("click", function() {
$("this").css({color:"#f790e8"})
$(".others:nth-child(1)").fadeOut("300")
$(".others:nth-child(2)").delay("150").fadeOut("300")
$(".others:nth-child(3)").delay("300").fadeOut("300")
$(".others:nth-child(4)").delay("450").fadeOut("300")
$(".tube1").delay("300").fadeIn("300")
$(".tube2").delay("450").fadeIn("300")
$(".tube3").delay("600").fadeIn("300")
$('#two').on("click", function() {
//this code will execute on second click and further clicks
$("this").css({color:"black"})
$(".others:nth-child(1)").delay("300").fadeIn("300")
$(".others:nth-child(2)").delay("450").fadeIn("300")
$(".others:nth-child(3)").delay("600").fadeIn("300")
$(".others:nth-child(4)").delay("750").fadeIn("300")
$(".tube1").fadeOut("300")
$(".tube2").delay("150").fadeOut("300")
$(".tube3").delay("300").fadeOut("300")
});
});
});
You'll probably have a better time setting a class on the element and using it to see which one of the two behaviors to trigger.
$(function () {
$("#two").on("click", function () {
const $this = $(this);
if ($this.hasClass("on")) {
//this code will execute on second click and further clicks
$this.css({ color: "black" });
$(".others:nth-child(1)").delay("300").fadeIn("300");
$(".others:nth-child(2)").delay("450").fadeIn("300");
$(".others:nth-child(3)").delay("600").fadeIn("300");
$(".others:nth-child(4)").delay("750").fadeIn("300");
$(".tube1").fadeOut("300");
$(".tube2").delay("150").fadeOut("300");
$(".tube3").delay("300").fadeOut("300");
} else {
$this.css({ color: "#f790e8" });
$(".others:nth-child(1)").fadeOut("300");
$(".others:nth-child(2)").delay("150").fadeOut("300");
$(".others:nth-child(3)").delay("300").fadeOut("300");
$(".others:nth-child(4)").delay("450").fadeOut("300");
$(".tube1").delay("300").fadeIn("300");
$(".tube2").delay("450").fadeIn("300");
$(".tube3").delay("600").fadeIn("300");
}
$this.toggleClass("on");
});
});

Waiting with $(document).ready, $(element).ready, and windows.load all trigger before content is ready

Would you please help me delay execution of my function until the content has loaded? I've streamlined my code to the essentials, bear with my typos:
function Phase1()
{
$(".Hidden").load("hidden.html");
$(window).load(Phase2());
/* I've also tried $(document).ready(Phase2()); */
/* and $(."Hidden").load("hidden.html",Phase2()); */
/* and window.onload... */
}
function Phase2()
{
var Loop;
var Source_Array = document.getElementsByClassName("Random");
for (Loop=0;Loop<Source_Array.length,Loop++)
{ alert(Source_Array[Loop].innerHTML; };
}
The Random class contains several items. On the first pass the alerts are never called (length is 0), on the 2nd iteration it's had time to load everything.
I see no errors in the console when executing.
I have a small and neat solution for your problem, all you need to do is,
Call a setInterval for very short span to check the element is present in DOM or not, if its not your interval will go on, once the element is present, trigger your functions and clear that interval.
code will look like this..
var storeTimeInterval = setInterval(function() {
if (jQuery('.yourClass').length > 0) {
//do your stuff here..... and then clear the interval in next line
clearInterval(storeTimeInterval);
}
}, 100);
The page will load the elements from top to bottom.
If you want your JS code to execute after all elements have loaded, you may try any of the following:
Move your script to the bottom of the page.
<html>
<head></head>
<body>
<!-- Your HTML elements here -->
<script>
// Declaring your functions
function Phase1()
{
$(".Hidden").load("hidden.html");
}
function Phase2()
{
var Loop;
var Source_Array = document.getElementsByClassName("Random");
for (Loop=0;Loop<Source_Array.length,Loop++)
{ alert(Source_Array[Loop].innerHTML; };
}
// Executing your functions in that order.
Phase1();
Phase2();
</script>
</body>
</html>
Bind your functions to document ready using Vanilla JS.
document.addEventListener("DOMContentLoaded", function() {
Phase1();
Phase2();
});
Bind your functions to document using jQuery.
$(document).ready(function() {
Phase1();
Phase2();
});

Why this .js script stops working and is causing a browser freeze?

I've downloaded this Drupal 8 template and the site is at www.plotujeme.sk. It has an responsive navigation with this .js script:
function sidebar_menu() {
var windowsize = jQuerywindow.width(),
jQuerynav = jQuery("nav"),
slide = {
clear: function () {
jQuerybody.removeClass('toggled');
jQuery('.overlay').hide();
jQuery('.easy-sidebar-toggle').prependTo("header");
//jQuery('#search').prependTo("body");
jQuery('.navbar.easy-sidebar').removeClass('toggled');
jQuery('#navbar').removeAttr("style");
},
start: function () {
jQuery('.overlay').show();
jQuerybody.addClass('toggled');
jQueryhtml.addClass('easy-sidebar-active');
jQuerynav.addClass('easy-sidebar');
jQuery('.easy-sidebar-toggle').prependTo(".easy-sidebar");
//jQuery('#search').prependTo("#navbar");
jQuery('#navbar').height(jQuerywindow.height()).css({
"padding-top": "60px"
});
},
remove: function () {
jQuerynav.removeClass('easy-sidebar');
}
};
if (windowsize < 1003) {
jQuerynav.addClass('easy-sidebar');
jQuery('.easy-sidebar-toggle').on("click", function (e) {
e.preventDefault();
if (jQuerybody.hasClass('toggled')) {
slide.clear();
} else {
slide.start();
}
});
/*
jQueryhtml.on('swiperight', function () {
slide.start();
});
jQueryhtml.on('swipeleft', function () {
slide.clear();
}); */
} else {
slide.clear();
slide.remove();
}
}
and:
jQuery(document).ready(function () {
"use strict";
sidebar_menu();
jQuery(window).resize(function () {
sidebar_menu();
});
});
Problem is, that if I open responsive navigation by clicking on hamburger button, it works several times and then it stops working, the page and a browser freezes or is unresponsive for a long time. I also noticed that (even in template preview) sometimes it does not work at all and nothing happens after clicking hamburger icon. When I resize window multiple times sometimes it works sometimes not.
Do you see any error in the script that could possibly cause this problem?
Update: I also tried to use jQuery('.easy-sidebar-toggle').off("click"); just before jQuery('.easy-sidebar-toggle').on("click", function() {...}); but got the same results.
jQuery(window).resize(function () {
sidebar_menu();
});
As a result, whenever sidebar_menu function changes the window size, this function is called again and again, like a recursion, hence the freezing
I think the reason might be the following lines in the resize handler:
jQuerynav.addClass('easy-sidebar');
jQuery('.easy-sidebar-toggle').on("click", ...
They are run every time the window is resized by even one pixel, so a few dozen times a second if you drag the window border. Not sure about the first line, whether it adds the class over and over, but the second line certainly adds an event handler multiple times and fills up the stack. That's the reason your browser freezes. It just can't process the hundreds of registered events.
Just a guess, though.

jQuery AUTOMATIC scroll (no button click) on document ready

I am creating a chat, everything works perfectly, it scrolls down when i click the "Send" button, but I want it to scroll all the way down when the document is ready. I have done this by adding the scrolling function to setInterval, but the problem with that is that the user basically cant scroll up to see previous chat messages because he gets scrolled down every 0.1 seconds. My code is:
$(function () {
//$("#messages").scrollTop($("#messages").prop("scrollHeight")); Doesnt work at all
function updateChat(){
$("#messages").load('chat/ajaxLoad.php');
//$("#messages").scrollTop($("#messages").prop("scrollHeight")); This works but the user cannot scroll up anymore
}
setInterval(function () {
updateChat();
}, 100);
$("#post").submit(function(){
$.post("chat/ajaxPost.php", $('#post').serialize(), function (data) {
$("#messages").append('<div>'+data+'</div>');
$("#messages").scrollTop($("#messages").prop("scrollHeight")); // This works but only when the user presses the send button
$("#text").val("");
});
return false;
});
});
Add this to your code.
var chat = $("#messages").html();
setInterval(function () {
updateChat();
if(chat !== $("#messages").html()){
$("#messages").scrollTop($("#messages").prop("scrollHeight"));
chat = $("#messages").html();
}
}, 2000);
I think this should work (didnt test), but there are some better ways you can optimise this like not saving the whole .html() into a variable.
The idea here is that it checks if the content is changed every 2 seconds. If it is, it scrolls down.
I see what's your problem and I have 2 ideas for you :
You scroll down only when a new message is post, for example with an Ajax request you could check if number of messages is > in compare with the last 0.1s, if yes you scroll if not you ignore.
You scroll down every 1-2s only if the scroll is at the maximum bottom position. If the scroll is not at the maximum you do not scroll. I feel this solution is better.
You need to seperate the actions on your application,
also you missed many checks that can make the application work properly and will
make it easy to maintain.
How i suggestion the code will look:
$(function () {
function updateMessages(){
var messages_before_update = $("#messages").html();
$("#messages").load('chat/ajaxLoad.php');
var message_after_update = $("#messages").html();
if(messages_before_update !== message_after_update){
scrollToBottom();
}
}
function scrollToBottom(){
var scroll_height = $("#messages").prop("scrollHeight");
var scroll_top = $("#messages").scrollTop();
if(scroll_height !== scroll_top){
$("#messages").scrollTop($("#messages").prop("scrollHeight"));
}
}
function addMessage(message){
$("#messages").append('<div>' + message + '</div>');
}
setInterval(updateMessages, 100);
$("#post").submit(function () {
$.post("chat/ajaxPost.php", $('#post').serialize(), function (data) {
addMessage(data);
scrollToBottom();
$("#text").val("");
});
return false;
});
});

Knockout/JavaScript Ignore Multiclick

I'm having some problems with users clicking buttons multiple times and I want to suppress/ignore clicks while the first Ajax request does its thing. For example if a user wants add items to their shopping cart, they click the add button. If they click the add button multiple times, it throws a PK violation because its trying to insert duplicate items into a cart.
So there are some possible solutions mentioned here: Prevent a double click on a button with knockout.js
and here: How to prevent a double-click using jQuery?
However, I'm wondering if the approach below is another possible solution. Currently I use a transparent "Saving" div that covers the entire screen to try to prevent click throughs, but still some people manage to get a double click in. I'm assuming because they can click faster than the div can render. To combat this, I'm trying to put a lock on the Ajax call using a global variable.
The Button
<span style="SomeStyles">Add</span>
Knockout executes this script on button click
vmProductsIndex.AddItemToCart = function (item) {
if (!app.ajaxService.inCriticalSection()) {
app.ajaxService.criticalSection(true);
app.ajaxService.ajaxPostJson("#Url.Action("AddItemToCart", "Products")",
ko.mapping.toJSON(item),
function (result) {
ko.mapping.fromJS(result, vmProductsIndex.CartSummary);
item.InCart(true);
item.QuantityOriginal(item.Quantity());
},
function (result) {
$("#error-modal").modal();
},
vmProductsIndex.ModalErrors);
app.ajaxService.criticalSection(false);
}
}
That calls this script
(function (app) {
"use strict";
var criticalSectionInd = false;
app.ajaxService = (function () {
var ajaxPostJson = function (method, jsonIn, callback, errorCallback, errorArray) {
//Add the item to the cart
}
};
var inCriticalSection = function () {
if (criticalSectionInd)
return true;
else
return false;
};
var criticalSection = function (flag) {
criticalSectionInd = flag;
};
// returns the app.ajaxService object with these functions defined
return {
ajaxPostJson: ajaxPostJson,
ajaxGetJson: ajaxGetJson,
setAntiForgeryTokenData: setAntiForgeryTokenData,
inCriticalSection: inCriticalSection,
criticalSection: criticalSection
};
})();
}(app));
The problem is still I can spam click the button and get the primary key violation. I don't know if this approach is just flawed and Knockout isn't quick enough to update the button's visible binding before the first Ajax call finishes or if every time they click the button a new instance of the criticalSectionInd is created and not truely acting as a global variable.
If I'm going about it wrong I'll use the approaches mentioned in the other posts, its just this approach seems simpler to implement without having to refactor all of my buttons to use the jQuery One() feature.
You should set app.ajaxService.criticalSection(false); in the callback methods.
right now you are executing this line of code at the end of your if clause and not inside of the success or error callback, so it gets executed before your ajax call is finished.
vmProductsIndex.AddItemToCart = function (item) {
if (!app.ajaxService.inCriticalSection()) {
app.ajaxService.criticalSection(true);
app.ajaxService.ajaxPostJson("#Url.Action("AddItemToCart", "Products")",
ko.mapping.toJSON(item),
function (result) {
ko.mapping.fromJS(result, vmProductsIndex.CartSummary);
item.InCart(true);
item.QuantityOriginal(item.Quantity());
app.ajaxService.criticalSection(false);
},
function (result) {
$("#error-modal").modal();
app.ajaxService.criticalSection(false);
},
vmProductsIndex.ModalErrors);
}
}
you could use the "disable" binding from knockout to prevent the click binding of the anchor tag to be fired.
here is a little snippet for that. just set a flag to true when your action starts and set it to false again when execution is finished. in the meantime, the disable binding prevents the user from executing the click function.
function viewModel(){
var self = this;
self.disableAnchor = ko.observable(false);
self.randomList = ko.observableArray();
self.loading = ko.observable(false);
self.doWork = function(){
if(self.loading()) return;
self.loading(true);
setTimeout(function(){
self.randomList.push("Item " + (self.randomList().length + 1));
self.loading(false);
}, 1000);
}
}
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js"></script>
Click me
<br />
<div data-bind="visible: loading">...Loading...</div>
<br />
<div data-bind="foreach: randomList">
<div data-bind="text: $data"></div>
</div>

Categories

Resources