Settimout not working inside For loop, acting weird? - javascript

Im trying to simulate a Typewriter effect with javascript.
Theorically it should work with my code:
function TypeWriteToDoc(txt, id, x){
document.getElementById(id).innerHTML = document.getElementById(id).innerHTML + txt.charAt(x);
}
function TypeWrite(txt,id){
for (var i = 0; i < txt.length; i++){
setTimeout(function() {
TypeWriteToDoc(txt, id, i);
}, 1000*(i+1));
}
}
That should be it, when i call TypeWrite("example", "p_test"); it should write each character of "test" in the "p_test" html. I think the problem its not on my code since when i call the function without using setTimeout it works like in the code below:
function TypeWriteWithNoSettimeout(txt, id){
for (var i = 0; i < txt.lenght; i++){
TypeWriteToDoc(txt, id, i);}
}

This is a common issue with var in for-loops with callback functions.
The easiest solution? Just use let instead. let has support in all major browsers.
function TypeWrite(txt,id){
for (let i = 0; i < txt.length; i++){
setTimeout(function() {
TypeWriteToDoc(txt, id, i);
}, 1000*(i+1));
}
}

Similar to the previous response but rather than appending original text along with div.innerHtml, I adjusted it to be just the text char which simulates more of a typewriter feel. To increase the delay, I multiplied the index with 1000 rather than adding it since the larger increments are more visible.
function TypeWriteToDoc(txt, id, i) {
setTimeout(function() {
var div = document.getElementById(id)
div.innerHTML +=txt.charAt(i)
}, 1000 * (i))
}
function TypeWrite(txt,id){
for (var i = 0; i < txt.length; i++) {
TypeWriteToDoc(txt, id, i)
}
}
TypeWrite('example', 'p_test')
<div id="p_test"></div>

Related

.mouseover in Javascript does not work when set to a function, but returns normally when using a new function

currently I'm using a for loop to iterate over some tags, and when I try to alter the
tags .mouseover in JS, it returns null.
let htmlElements = document.getElementsByTagName("a");
for (let i = 0; i < 22; i++) {
for (let j = 0; j < 20; j++) {
htmlElements[i * 20 + j].onmouseover = onTileHovered(j, i, this);
}
}
If I console.log, it prints out null.
A solution for this is putting set it and make the function there.
let htmlElements = document.getElementsByTagName("a");`
for (let i = 0; i < 22; i++) {
for (let j = 0; j < 20; j++) {
htmlElements[i * 20 + j].onmouseover = function () {
// function stuff
};
}
}
This works, however, I'm worried that this would be making multiple copies of the function which would be pretty inefficient, and was wondering why the other way doesn't work.
What you're doing is perfectly fine. Adding 400 event listeners will have no visible impact at all on an application on modern devices, even on potato-level phones. Computers can handle repetitive tasks of much higher intensity.
If you were adding enough listeners that it was an issue to consider, then an approach would be to turn the elements into an array, and whenever a container of all those <a>s is moseovered, check the index of the element in the array.
const aContainer = document.querySelector('.container'); // replace this with appropriate selector
const anchors = [...aContainer.querySelectorAll('a')];
aContainer.addEventListener('mouseover', (e) => {
const { target } = e;
const a = target.closest('a');
if (!a) return;
const index = anchors.indexOf(a);
// do stuff with the clicked anchor and the index
// modulo can be used to find i and j, if needed
});

Pass parameters to dynamically created Gulp task [duplicate]

Consider such loop:
for(var it = 0; it < 2; it++)
{
setTimeout(function() {
alert(it);
}, 1);
}
The output is:
=> 2
=> 2
I would like it to be: 0, 1. I see two ways to fix it:
Solution # 1.
This one based on the fact that we can pass data to setTimeout.
for(var it = 0; it < 2; it++)
{
setTimeout(function(data) {
alert(data);
}, 1, it);
}
Solution # 2.
function foo(data)
{
setTimeout(function() {
alert(data);
}, 1);
}
for(var it = 0; it < 2; it++)
{
foo(it);
}
Are there any other alternatives?
Not really anything more than the two ways that you have proposed, but here's another
for(var it = 0; it < 2; it++)
{
(function() {
var m = it;
setTimeout(function() {
alert(m);
}, 1);
})();
}
Essentially, you need to capture the variable value in a closure. This method uses an immediately invoked anonymous function to capture the outer variable value it in a local variable m.
Here's a Working Demo to play with. add /edit to the URL to see the code
With the let keyword you can get around this completely:
for(let it = 0; it < 2; it++)
{
setTimeout(function() {
alert(it);
}, 1);
}
Similar to above solution but self invoking inside of setTimeout function
for(var it = 0; it < 2; it++)
{
setTimeout(function(cur) {
return function(){
alert(cur);
};
}(it), 1);
}
Similar to the other solutions, but in my opinion cleaner:
for (var it = 0; it < 2; it++) {
// Capture the value of "it" for closure use
(function(it) {
setTimeout(function() {
alert(it);
}, 1);
// End variable captured code
})(it)
}
This keeps the same variable name for the capture, and does it for the entire loop, separating that from the logic of the timeout setup. If you want to add more logic inside the block, you can trivially do that.
The only thing I don't like about the solution is the repeat of "it" at the end.

Strings in array not appearing as strings in HTML

First, my HTML:
<p id="p"></p>
<input id="input" />
<button onclick="fill()">Fill</button>
Then my Javascript:
function fill() {
var x = document.getElementById('input').value;
var y = x.split('');
for (var i = 0; i < y.length; i++) {
if (i == 0) {
setTimeout(function() {
document.getElementById('p').innerHTML = y[i];
},(i * 50));
}
setTimeout(function() {
document.getElementById('p').innerHTML += y[i];
},(i * 50));
}
}
What it does is take text from a textfield, cut each character including spaces into an array, then loop through the array, displaying each character at 50ms intervals. The effect is supposed to look like it is typing itself out.
The effect works fine, but the values of the array don't seem to be. If I were to type "abc123" then I would expect that to come right back out, but instead I get:
undefinedundefinedundefinedundefinedundefinedundefined
Using console.log(), when I check the array it looks fine, when I check the typeof of the individual array values I get string, but when I check the typeof for the array, I get object. Maybe this is what's wrecking it...
I have tried use toString() on the y[i] which just spits out "[object Window]", I have tried defining the array like this var y = new Array; and then doing the split(), nothing works. I am at a complete loss. Really would love some help here. Thanks!
I believe there must be closure problem. Try this js code. I wrapped everything inside loop in a IIFE function. I think it is well explained here Please explain the use of JavaScript closures in loops
<script>
function fill() {
var x = document.getElementById('input').value;
var y = x.split('');
for (var i = 0; i < y.length; i++) {
(function(i){
if (i == 0) {
setTimeout(function() {
document.getElementById('p').innerHTML = y[i];
},(i * 50));
}
setTimeout(function() {
document.getElementById('p').innerHTML += y[i];
},(i * 50));
})(i);
}
}
</script>
By calling setTimeout you're scheduling something to happen in the future. By the time the function you're delaying with setTimeout is executed, the loop has executed all the way through and i is now equal to y.length. So if you input test when the function executes it tries to add y[4] as a letter which is undefined. To fix it, you can do something like this:
function fill() {
var x = document.getElementById('input').value;
var y = x.split('');
console.log(y);
for (var i = 0; i < y.length; i++) {
timeOutAdd(y[i], i*50)
}
}
function timeOutAdd(c, delay){
setTimeout(function() {
document.getElementById('p').innerHTML += c;
}, delay);
}
<p id="p"></p>
<input id="input" />
<button onclick="fill()">Fill</button>
By adding the timeOutAdd function which is called immediately instead of delayed we can hang on to the values of the parameters until the setTimeout function runs.
Note: I also removed the second call to setTimeout, it wasn't necessary and caused a bug where the first character of the string was output twice.
This solves the closure problem, plus it simplifies the logic for outputting the data. No need for splitting the string into an array:
function fill() {
var x = document.getElementById('input').value,
p = document.getElementById('p');
for(var i = 0; i < x.length; i++) {
(function(i) {
setTimeout(
function() {
p.innerHTML= x.substr(0, i+1);
},
i*50
)
})(i);
}
}
<p id="p"></p>
<input id="input" value="abcde" />
<button onclick="fill()">Fill</button>

jQuery add class and sleep then remove class

I have an image map which has several divs on it as city points. And I wrote a class in css to animate those points' color, so I can add that class through jQuery, wait sometime and remove the class. The goal is to animate those points randomly (add class, wait, remove class at random), but currently I am stuck with waiting before removing the class. I tried different solutions, including those that are posted on this site, but no result. Hre is the code:
function builtCities() {
if ($('body.page-service-map').size()) {
var content = $('#region-content .content'),
cityDot = '<div class="city-dot"></div>',
cities = [
'moscow',
'saint-petersburg',
'krasnodar',
'rostov-na-donu',
'tyumen',
'omsk',
'irkutsk'
];
for (var i = 0; i < 7; i++) {
content.append(cityDot);
}
$('body.page-service-map .city-dot').each(function (index) {
$(this).addClass(cities[index]);
});
// animation
for (var j = 0; j < cities.length; j++) {
function partA(partB) {
$('.city-dot').eq(j).addClass('animate');
window.setTimeout(partB, 1000);
} partA(partB);
function partB() {
$('.city-dot').eq(j).removeClass('animate');
}
}
}
} builtCities();
It's not working because of closures. Do it like this:
for (var j = 0; j < cities.length; j++) {
$('.city-dot').eq(j).addClass('animate');
window.setTimeout((function (j) {
return function () {
$('.city-dot').eq(j).removeClass('animate');
};
}(j)), 1000);
}
Your current one doesn't work because your j variable will be persisted and will actually be equal to cities.length at the time you're calling partB. To get around this, the above calls a function passing in j which will return another function using a separate variable (the parameter j) that will use the correct index.

Can't manage to sleep inside a loop

I want to pause 1 second for every time it loops, it is usually easy to do similar pauses on other cases, but when working with loops, it seems it get harder:
for (var i=0 ; i < 10 ; i++) {
document.write (i + "<br>");
// I want to wait 1 second here
}
This is one example of my thousands failed attempts:
function writeMsg (index) {
document.write (index + "<br>");
}
for (var i=0 ; i < 10 ; i++) {
setTimeout (writeMsg(i), 1000);
}
Any ideas of how to get this to work?
This function works more like a normal for loop while it isn't
You need to take into account that a for gets 3 arguments inbetween semicolons.
Before starting (ie var i=0 you define a variable)
A condition before running the code again (ie i < 10 while i is under 10)
An action everytime it finishes the code again (i++ add one to i)
Code
(function() {
// Define a variable
var i = 0,
action = function() {
// Condition to run again
if (i < 10) {
document.write(i + "<br>");
// Add one to i
i++;
setTimeout(action, 1000);
}
};
setTimeout(action, 1000);
})();
Here is a jsfiddle for this code demonstrating its working:
http://jsfiddle.net/sg3s/n9BNQ/
You pass the return value of a function call to setTimeout instead of a function. Try the following code:
for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() {
writeMsg(i);
}, 1000*i);
})(i);
}
In case you wonder why the call is wrapped inside an anonymous function: Without that function each setTimeout callback would receive the same i so when the callbacks fire it would always be 10. The anonymous function creates a new i inside that is not connected to the loop variable.
Classic function-in-a-loop problem. One archetypal solution:
function createCallback(i) {
return function () {
writeMsg(i);
};
}
function writeMsg (index) {
document.write (index + "<br>");
}
for (var i=0 ; i < 10 ; i++) {
setTimeout (createCallback(i), 1000*i);
}
The 10 timeouts are all based on the time that setTimeout() is called. So, they are all triggered at the same time.
for (var i=0; i < 10; i++) {
(function(idx){
setTimeout(function(){
document.write(idx+"<br/>");
},1000*idx);
})(i);
};
try this it will definitely help who all are think how to make it work wait property inside For Loop...
try this code in this URL http://www.shopjustice.com/the-collections/C-10329.
var var2;
var tmp;
var evt;
var i=0;
var res = document.getElementsByClassName('mar-plp-filter-content nav nav--stacked')[0].children.length;
tmp = document.getElementsByClassName('mar-plp-filter-content nav nav--stacked')[0].children;
function myfunc()
{
if(i<res)
{
var2 = tmp[i].getElementsByTagName("span")[0].getElementsByClassName("inverted")[0];
// alert(var2.innerHTML);
var evObj = document.createEvent('MouseEvents');
evObj.initEvent( 'mouseover', true, false );
var2.dispatchEvent(evObj);
var2.style.backgroundColor="GREEN";
i++;
setTimeout(myfunc,3000);
}
};
setTimeout(myfunc,3000);

Categories