This question already has an answer here:
JavaScript setTimeOut doesn't seem to work like I expect
(1 answer)
Closed 4 years ago.
Relative newcomer to JS trying to keep my code class-oriented for the sake of best practice. I found a piece of code here for a text cycling effect I wanted to employ on a project. Using the code as-is within my main App.js worked fine. I then attempted to make it class/module oriented for later possible recycling, but thus far I've hit a brick wall. Passing console logs instead I've noticed that my attempts to use SetInterval, both within the class and in my App.js file calling the class into my HTML, don't seem to work, never properly looping more than once outside of isolating it.
Here's the current state of my code, possibly far removed from what I was doing previously, but it should give an idea where my head's at.
TextCycle.js:
import $ from 'jquery';
class TextCycle{
constructor(){
this.cycledText = $('.cycled-text');
this.i = 0;
}
cycle(){
//this.cycledText.removeClass('cycled-text--active');
//this.cycledText.eq(this.i).addClass('cycled-text--active');
this.i++;
console.log(this.i);
}
}
export default TextCycle;
App.js:
import TextCycle from './components/TextCycle';
var textCycle = new TextCycle();
setInterval(textCycle.cycle(), 3000);
Given I have a working version this isn't a critical issue, but given the point was practicing this sort of JS organization and modularization due to how I was taught, any insight on making this work is immensely helpful!
There is a problem with the way you're using setInterval() , along with your call to .cycle().
In short, you will want to update your code that calls setInterval so that cycle() is only called during the callback. Something like this would do:
setInterval(function() { textCycle.cycle() }, 3000);
One of the reasons your current code fails is that, textCycle.cycle() is called immediately, during this line:
setInterval(textCycle.cycle(), 3000); // Notice the () after cycle?
// That's causing the cycle
// method to be called immediately
By placing this in a callback function, as shown above, this does a few things:
it ensures that cycle() is invoked from the correct context (ie the context of your textCycle object. That basically means that when your use this inside of cycle(), then this will be the textCycle object, rather than the global object
it ensures that the cycle() method only gets invoked during the setInterval "callback", which is called on your set internal, rather than being called immediately
Hope this helps!
Related
There is a widget in the POS(point of sale) called PaymentScreenWidget and inside that there's a customer_changed method which is called when the customer is changed.
Suppose that I want to call a method after this method is called then how can I do this without interfering with this code?
Interfering with this code leaves a lot of trouble in many case so are there any ways to achieve this?
I want to append some text to it but since there are many module who try to change those code or override it I want to avoid doing it and try to call my method after that method has been called.
You'll have to do some code instrumentation.
For example,
let old_customer_changed = customer_changed;
customer_changed = function(){
// my awesome code
old_customer_changed();
}
You'll have to implement similar thing in whatever modules you are using.
So, I've been trying to understand a specific js library by running it through my browser's debugger, and something happens that just confuses me.
I first encountered this in the Phaser game library, but I've seen it another library as well. I'll use Phaser as an example:
<script>
(function(){
var game = new Phaser.Game(800, 600, Phaser.CANVAS, '');
game.state.add('Game', Game);
game.state.start('Game');
})();
</script>
So that anonymous function finishes setting some things up, and I step over and out of that function, and after a couple more steps (the pointer just sitting at the top of the html doc in the meantime) my program out of nowhere ends up here:
Phaser.Device._readyCheck = function () {
var readyCheck = this._readyCheck;
....
}
It didn't look like anything within my flow of control called that function, so how did I get here? What's calling this function? I've read a bit about 'asynchronous functions' and that sounds like a pretty good explanation, but the stuff I've glanced at on Google don't really explain anything well, so I can't say I understand it enough to be sure. I'm relatively new to JavaScript.
The library sets up event handlers for various events which then get called asynchronously. In this case, event handlers are setup to to watch for the completion of the loading of the DOM, among other things, so that the internal state of the library can be initialized. You can see how it happens in the source
src/system/Device.js:Phaser.Device._readyCheck
and Phaser.Device.whenReady in the same file. With a little searching you can easily find who calls whenReady.
The function you pointed out at the top is a self-executing function. (); causes everything contained in the preceding parentheses to be executed immediately.
Essentially i have three different classes that can be applied to an element on my page. Each holds a different background image and i want to use JQuery to find out which class is currently there and the remove that class and replace it with another. and then set a timer on it to run the function every 5000ms
this is what my code looks like
$(document).ready(
function(){
var toggleImage = function(){
if($(".home").hasClass("home1")){
$(".home").removeClass("home1").addClass("home2") }
elseif($(".home").hasClass("home2")){
$(".home").removeClass("home2").addClass("home3")
}
elseif($(".home").hasClass("home3")){
$(".home").removeClass("home3").addClass("home1")
}
}
setInterval(toggleImage(), 5000);
});
The class of home will always be there and im just trying to add and remove the other classes on the element. Im coming from PHP so im thinking maybe my syntax could be off.
You have to pass a function to setInterval. Currently you are calling toggleImage and pass its return value, which is undefined.
Don't call the function:
setInterval(toggleImage, 5000);
At least in Firefox you would have gotten a runtime error:
Error: useless setInterval call (missing quotes around argument?)
Im coming from PHP so im thinking maybe my syntax could be off.
You are right, elseif should be else if. This is a syntax error and you should have seen it in the console as well.
Learn how to debug JavaScript to find syntax and runtime errors on your own.
You want to pass setInterval a function. Calling toggleImage() runs the function and ends up passing setInterval the result. The correct syntax should be:
setInterval(toggleImage, 5000);
I encountered a problem when I was reading the page
http://processingjs.org/articles/PomaxGuide.html
Please note the section "Processing.js as javascript graphics library":
the setup method is rewrote as following:
pjs.setup = function() {
pjs.size(200,200);
// we want to turn off animation, because this is a demo page and it
// would use cpu while not being looked at. Only draw on mousemoves
pjs.noLoop();
}
and Finally, we can call setup() to kickstart the sketch.
pjs.setup();
The question is that the setup() is already override, how can the processing run the loop? there's no such code to invoke draw() loop in setup().
The draw() method gets called automatically according to the value of a field called doLoop inside the Processing class (source code: https://s3.amazonaws.com/github/downloads/processing-js/processing-js/processing-1.4.1.js).
When you call noLoop() you are setting this value to false, disabling in fact the loop. If don't do this, then Processing.js behaves in its default way executing the loop: there's no need to state it explicitly in the setup() method.
Is there a way to include a javascript file only once or declare a function only once? The issue I am having is that I have an HTML module that contains a javascript include. Well this module is loaded in a loop, and therefore that file is loaded multiple times. I've worked out most of the kinks, but what bothers me is that I know the same function is getting created multiple times, and this look can be as many as 30 iterations. To me, I don't like the fact that the same function is getting created over and over. Should I care? Is there a way I can prevent this? I know I can detect when a function exists, can I put the function declaration in between an if statement?
Update
I've tried out one of the suggestions:
if(typeof btnSendInvite_click != 'function')
{
function btnSendInvite_click()
{
alert("#invite_guest_" + $(this).attr("event_id"));
return false;
}
}
but that doesn't work. I've also tried
if(!btnSendInvite_click)
{
function btnSendInvite_click()
{
alert("#invite_guest_" + $(this).attr("event_id"));
return false;
}
}
but it doesn't work. What happens is that I have this line:
$(document).ready(function()
{
$(".btnSendInvite").bind("click", btnSendInvite_click);
});
and when the button gets clicked, that functions is executed six times, which is the amount of times that the file was included which tells me that the function is being created multiple times... I think.
Update
So after a lot of struggling, this problem is turning into something different than what I thought. The bind is being called multiple times, so it's getting bound multiple times, and therefore calling the function multiple times. I guess my next question is, is there a way to bind a function to a control only once? I've tried the jquery "one" already and it doesn't work.
Yes, you can (run on jsfiddle).
if (!window.myFunction) {
window.myFunction = function() {
//...
}
}
Edit: In your case it would be:
if (!window.btnSendInvite_click) {
window.btnSendInvite_click = function() {
alert("#invite_guest_" + $(this).attr("event_id"));
return false;
}
}
The call to bind() also has to be somewhere in that conditional block.
Note: The following variant won't work, at least not on all browsers:
if (!window.myFunction) {
function myFunction() {
//...
}
}
Edit 2: For your update:
Declare a variable when you call bind.
if (window.iBoundThatStuff!=true) {
iBoundThatStuff=true;
//call bind() here
}
Having JS included in a loop is ridiculous. Move your JS out of the loop.
JS can tell if function was defined but fixing bad server side loop in JS is definitively a bad practice.
Yes you should worry about not including your script file several times and not to declare the function several times...
For the first part, you may want to look into changing your html structure so the js file is only included once (even though js files are cached by the browser, and the second time may not actually go to the server -- depending of several factors... there's still a penalty)
Now as for declaring your function only once, remember that functions are also object (1st class citizens) in js, so you can test if a function is already define as if you were testing an object.... if(!window.myFunc) { window.myFunc = function(){} }...
You may want to look a bit into functions and scoping in js.. here are some links
http://robertnyman.com/2008/10/09/explaining-javascript-scope-and-closures/
http://www.yuiblog.com/blog/2010/02/24/video-crockonjs-3/
http://www.slideshare.net/douglascrockford/crockford-on-javascript-act-iii-function-the-ultimate