I'm getting the error that "txtname" is undefined.
let i = 0;
let txtOne = 'Hi';
let txtTwo = 'My name is Sarah';
let txtThree = "and I'm learning web development";
let speed = 200;
let firstdiv = document.querySelector(".firstOne");
let nextdiv = document.querySelector(".nextOne");
let lastdiv = document.querySelector(".lastOne");
function typeWriter(txtname, divname) {
if (i < txtname.length) {
divname.innerHTML += txtname.charAt(i);
i++;
setTimeout(typeWriter, speed);
}
}
window.onload = typeWriter(txtOne, firstdiv);
firstdiv.addEventListener("animationend", typeWriter(txtTwo, nextdiv));
nextdiv.addEventListener("animationend", typeWriter(txtThree, lastdiv));
Why is txtname coming up as undefined? Shouldn't it get replaced by whatever I pass as an argument in my typeWriter function?
Why isn't the typeWriter function looking at txtOne.length or txtTwo.length etc?
I'm still in the process of learning javascript so please excuse me if this is a basic error.
setTimeout(typeWriter, speed) means that in 200 ms, typeWriter will be invoked with no arguments. The arguments from the previous invocation are not carried forward automatically to the next invocation, you need to supply them. You can do so with an anonymous function:
setTimeout(function () { typeWriter(txtname, divname) }, speed)
While you're fixing this, you should probably also move state like i into the function, rather than depending on global state. You can do so by accepting i as an argument, but giving it a default value of 0:
function typeWriter(txtname, divname, i) {
i || (i = 0);
if (i < txtname.length) {
divname.innerHTML += txtname.charAt(i);
setTimeout(function () { typeWriter(txtname, divname, i + 1) }, speed);
}
}
This is a common pattern with recursive functions.
Another issue is the way you are setting the event handlers. You are actually setting the returned value of the typeWriter function as the event handler instead of the function itself. You should remove the invocation operator, i.e. window.onload = typeWriter, but since you want to call the function with specific parameters, you need to wrap the code with another function:
window.onload = function() { typeWriter(txtOne, firstdiv) };
firstdiv.addEventListener("animationend", function() { typeWriter(txtTwo, nextdiv) });
nextdiv.addEventListener("animationend", function() { typeWriter(txtThree, lastdiv) });
Related
I'm not sure if what i am trying to do is possible, or if there's an easier way to do what I'm trying to do.
I have the following code:
<script>
function TitleSwitch() {
var counter = 0,
fn = function () {
var array = ['Value1','Value2','Value3'];
$(document).prop('title', array[counter]);
counter++;
counter %= array.length;
};
fn();
return fn;
}
setInterval(TitleSwitch(), 5000);
</script>
It rotates the page title between the three variables, Value1, Value2, and Value3 every 5 seconds. This is working fine.
However, on the same page there is some ajax script that is polling for other information related to the app.
What I am trying to do is use some of the data returned from the polling script to change the values in the title switching function.
So, as an example, the poll data may return Value4, Value5, and Value6 instead.
So in the code above, is there any way to replace the values in
var array = ['Value1','Value2','Value3'];
from another function, outside of the title switching function?
So, say I have a function called pollingDone() that is called each time the polling data is returned, how can I change the values of "array" in TitleSwitch() from within pollingDone() after TitleSwitch() is already running using setInterval?
basically, what I was trying to do is keep TitleSwitch running, but just replace the values used.
The reason I was trying to do it this way is because the titles are switched between the three values every 5 seconds, however the polling script runs every 10 seconds. So if I started the TitleSwitch() function over each time the polling script completes, the third value would never be shown in the title. The first two would show, the polling script would run, and then the titles would start over. So I was hoping to keep the TitleSwitch() function running as-is, and just replace the values it is using.
You can do that by exposing the array in the fn function to the outside context.
Here is an example:
function TitleSwitch() {
var counter = 0;
this.array = ['Value1','Value2','Value3'];
var self = this;
this.fn = function () {
$(document).prop('title', self.array[counter]);
console.log(self.array[counter]);
counter++;
counter %= self.array.length;
};
this.fn();
}
var switcher = new TitleSwitch()
setInterval(switcher.fn, 500);
function asyncFn(){
switcher.array[0] = "changed title1";
}
setTimeout(asyncFn, 1000)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Pass it in the constructor so you can control the access level from outside.
In the example:
myArray is defined outside the closure that TitleSwitch creates.
When editing its values, the next iteration will use the updated contents.
Like so:
function TitleSwitch(array) {
var counter = -1,
fn = function () {
counter++;
counter %= array.length;
// Move to bottom to prevent errors when using a shorter array
console.log(array[counter]);
};
fn();
return fn;
}
var myArray = ['Value1','Value2','Value3'];
setInterval(TitleSwitch(myArray), 1000);
myArray[1] = "TEST";
myArray[2] = "TEST2";
I think you will have to get your variable out of your function scope, something like this:
var titles = ['Value1', 'Value2', 'Value3'];
function TitleSwitch() {
var counter = 0,
fn = function () {
$(document).prop('title', titles[counter]);
counter++;
counter %= titles.length;
};
fn();
return fn;
}
setInterval(TitleSwitch(), 5000);
// Here, you can modify your titles in an ajax call
There is no way to replace array that is defined as a local variable inside fn. If you pull it out to outside of TitleSwitch, you can just give it a new value. Alternately, you can use a property on fn, or construct a more complex object, to avoid polluting the environment.
You also want to raise the modulo line to the start of fn: e.g. if you have a 5-element list with counter being 4 and you replace array with a 2-element list, your code would break.
var array = ['Value1','Value2','Value3'];
function TitleSwitch() {
var counter = 0,
fn = function () {
$(document).prop('title', array[counter]);
console.log(array[counter]);
counter++;
counter %= array.length;
};
fn();
return fn;
}
setInterval(TitleSwitch(), 5000);
function pollingDoneCallback(data){
if(data){
array=[];
for(var i=0;i<data.length;i++)
array.push(data[i]);
}
}
pollingDoneCallback(['val5','val6']);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I'm trying to call a function without re-initializing (hope I used the correct word here) it every time I call it. So the first time it gets called, it should initialize, but after its initialized, it should just use that reference.
Here's the code I'm trying to do it with.
JSFiddle
console.clear();
function mainFunction(e) {
var index = 0;
function subFunction() {
console.log(index++);
}
return subFunction();
}
window.addEventListener('click', mainFunction)
index should increase by one every time mainFunction gets called. The obvious solution, is to make index a global variable (or just out of mainFunction). But I need index to stay inmainFunction`.
How can I make index increment every time (using the same reference) mainFunction gets called?
I tried assigning mainFunction to a variable, then calling the variable in the event listener,
var test = mainFunction;
window.addEventListener('click', test)
but that didn't work. The results were the same.
You should correct the code as follows;
console.clear();
function mainFunction(e) {
var index = 0;
function subFunction() {
console.log(index++);
}
return subFunction; // <<< don't invoke subfunction
}
window.addEventListener('click', mainFunction()) // <<< invoke mainfunction
maybe try closures?
var main = (function () {
var index = 0;
return function () {return index += 1;}
})();
main()
main()
//index should be 2...
explain-
The variable main is assigned the return value of a self-invoking function.
The self-invoking function only runs once. index initialize only once.
If you don't want to make index global (or one scope higher regarding mainFunction), you can use a closure:
var mainFunction = (function () {
var index = 0;
return function () {return console.log(index++);}
})();
<button onclick="mainFunction()">Click</button>
Using OOP concept is the proper way to achieve this. The following should help you.
If you want to do it in ES6 way follow this babel example
var mainFunction = function(val) {
this.index = val //initialize this with the fn parameter or set a atatic value
}
mainFunction.prototype.subFunction = function() {
return this.index++
}
var instance = new mainFunction(0)
window.addEventListener('click', function() {
console.log(instance.subFunction())
})
<p>Click to see the result </p>
How can I pass a parameter to a function within CasperJS's evaluate?
//Should be logged in at this point
casper.then(function() {
var counter = 0;
var cap = 500;
this.evaluate(function(counter) {
var children = $('.companies-using-service').children();
while (counter < children.length) {
child = children[counter];
console.log($(child).find('a').attr('data-hint'));
counter++;
}
}, counter);
});
};
var scrapeClients = function(counter) {
var children = $('.companies-using-service').children();
while (counter < children.length) {
child = children[counter];
console.log($(child).find('a').attr('data-hint'));
counter++;
}
}
Above, I am able to pass parameters in using an unamed function. However, I wish to pass in the function scrapeClients to the evaluate function. In that case, I tried the following this.evaluate(scrapeClients(counter), counter). However, this does not work and the error says that it could not find $ variable.
Functions are first-class citizen in JavaScript. You can treat them in the same way as variables. You can pass them around. This means that you don't want
this.evaluate(scrapeClients(counter), counter)
but rather
this.evaluate(scrapeClients, counter)
In the first case, you're actually calling the function directly. Since the function uses some page properties that are only available inside of casper.evaluate, this will throw an error and stop the script.
I have a timeout which calls a function until 100% progress is complete. It then executes the function I have assigned to it. Only the value that was given to it is undefined or at least part of it.
I'm not sure at which stage the code is losing the value being passed, thus making it return undefined but I have made a JS Fiddle with it in action for you to see it:
JS Fiddle
My end result is to receive the value correct then remove the given element like so:
function rmv_div(div_id) {
//div_id is not properly defined so cannot find the div.
document.getElementById('result').innerHTML = div_id;
var div = document.getElementById(div_id);
div.parentNode.removeChild(div);
}
The problem is that the variable i used inside func is created outside the scope of that function, and is increased at each iteration. Then, when you call func at the end, i equals array.length, so array[i] is undefined.
You can solve it creating another variable at each iteration that you won't increase:
Solution 1:
Demo: http://jsfiddle.net/qJ42h/4/ http://jsfiddle.net/qJ42h/11/
for (var i = 0; i < array.length; i++) {
var bar = document.getElementById('bar' + array[i]),
text = document.getElementById('text' + array[i]),
remove = 'wrap' + array[i],
j = i;
do_something(bar, text, function () {
rmv_div('id' + array[j]);
}, 1);
}
Solution 2
Demo: http://jsfiddle.net/qJ42h/8/ http://jsfiddle.net/qJ42h/12/
for (var i = 0; i < array.length; i++) {
var bar = document.getElementById('bar' + array[i]),
text = document.getElementById('text' + array[i]),
remove = 'wrap' + array[i];
do_something(bar, text, (function(i) {
return function(){ rmv_div('id' + array[i]); }
})(i), 1);
}
The problem here is that you didn't isolate the loop variable i inside the closure. However, this can be solved much more elegantly by using objects.
First off, I'm introducing the object that will encapsulate what you want; it gets initialized with a bar element and a function to call when it's done counting to 100. So, I'll call it BarCounter:
function BarCounter(element, fn)
{
this.element = element;
this.fn = fn;
this.text = element.getElementsByTagName('div')[0];
this.counter = 0;
}
This is just the constructor; it doesn't do anything useful; it resolves the text element, which is simply the first <div> tag it can find underneath the given element and stores that reference for later use.
Now we need a function that will do the work; let's call it run():
BarCounter.prototype.run = function()
{
var that = this;
if (this.counter < 100) {
this.text.innerHTML = this.counter++;
setTimeout(function() {
that.run();
}, 70);
} else {
this.fn(this.element);
}
}
The function will check whether the counter has reached 100 yet; until then it will update the text element with the current value, increase the counter and then call itself again after 70 msec. You can see how the reference to this is kept beforehand to retain the context in which the run() function is called later.
When all is done, it calls the completion function, passing in the element on which the BarCounter object operates.
The completion function is much easier if you pass the element to remove:
function removeDiv(element)
{
element.parentNode.removeChild(element);
}
The final step is to adjust the rest of your code:
var array = [1];
for (var i = 0; i < array.length; ++i) {
var bar = new BarCounter(
document.getElementById('bar' + array[i]),
removeDiv
);
bar.run();
}
It's very simple now; it creates a new BarCounter object and invokes its run() method. Done :)
Btw, you have the option to remove the element from within the object as well; this, of course, depends on your own needs.
Demo
Can someone help me rectify the issue related to the setInterval? I'm fairly new to JavaScript, I'm not sure what's wrong here. I have this block in my page:
GlobalTicker.prototype.TickElements = function () {
this.timer = setInterval(this.initializeElement.apply(this) , 1000);
};
GlobalTicker.prototype.initializeElement = function () {
for (var i = 0; i < this.tickerElements.length; i++) {
var existingRun = this.tickerElements[i].secs;
var elementId = $('#' + this.tickerElements[i].id + ' .comment-editor').find('.ticker');
existingRun -= 1;
$(elementId).text(existingRun);
if (existingRun === 0) {
$(elementId).remove();
this.tickerElements.splice(i, 1);
if (this.tickerElements.length == 0) clearInterval(this.tickerElements.timer);
}
}
};
Then somewhere in the code, I have this call in a function
var objTicker = new GlobalTicker();
CommentManagement.prototype.createComment = function (domObject) {
objTicker.TickElements();
};
This function call actually invokes the setInterval function and runs the first iteration and jumps to the initialiseComment(); but once this block is executed, on the next interval, instead of executing the initialiseComment(); again, it jumps back to my function call CreateComment();. What am I doing wrong here?
setInterval() requires a function reference. You were calling the function itself and passing the return result from executing the function (which was undefined) to setInterval(). Since that return value is not a function, there was no callback function for setInterval() to call. Thus, your method was executed once when you first called it, but couldn't be called by the interval.
To fix it, you should change from this:
this.timer = setInterval(this.initializeElement.apply(this) , 1000);
to this:
var self = this;
this.timer = setInterval(function() {self.initializeElement()}, 1000);
Note, the value of this will also be different in the setInterval() callback than the value you want so the one you want is saved here in self so it can be referenced from that. There's also no need to use .apply() in this case because calling a method on an object will automatically set the this pointer as needed.