how the code is executed? - javascript

setInterval((function() {
var index = -1;
return function() {
var all = $('#latest a');
if (index >= 0) {
all.eq(index).hide();
}
index++;
if (index == all.length) {
index = 0;
}
all.eq(index).show();
};
})(), 1000);
how the code is executed? when index = -1; it not fits the first if condition, then the code goes to execute index++; now the index=0, then which step the code will be executed? if (index >= 0) or if (index == all.length) why?
i can't follow the first parameter of the setInterval well. could you explain it with more datails. thank you,

SetInterval call the function in the first parameter every second.
I will split you code into a semantically equivalent one to be more clear
timedFn = (function() {
var index = -1;
return function() {
var all = $('#latest a');
if (index >= 0) {
all.eq(index).hide();
}
index++;
if (index == all.length) {
index = 0;
}
all.eq(index).show();
};
})();
setInterval(timedFn, 1000);
timeFn is a closure and then the index variable value is retained between the different calls.
At the first call, index is -1, and then the execution skip the first if and the control go to the index++ instruction (likely the '#latest a` link are all hidden at the first run so you don't have to hide anything)
Now, a digression before we can continue:
You should know that eq method is applied to a collection
of elements and retrieve the zero based nth element of the collection it is applied to.
If you have a collection of three elements you can address them by eq(0)..eq(2) and
this is why you step into this >strange if. it is a guard that means if you pass over
the last element of my collection >the restart from the first one.
The updated index now contains 0 and, if the collection is empty you will have a bug as the index remains unchanged (index=0 but index is already zero) and the show will attempt to display a non existent element.
By the last instruction the first link is displayed and the execution stops.
After a second the function is called a second time but this time the index star at 0, not -1 and then the execution is repeated:
hide the first element
add 1 to index
if there is just an anchor in your set then reset the index to zero
show the second elements
the function ends
After another seconds the function is called again but you should already know how it works.
You can google on closures to have an indeep view of their inner working or just refer, as a starting point, to How do JavaScript closures work? on Stackoverflow itself.

The first parameter to setInterval, which when simplified looks like this:
(function() {
var index = -1;
return function() {
// some code that can access index
};
})()
defines an anonymous function expression that is immediatedly executed (note that at the end of the function definition it has () causing the execution). When this outer function is executed it returns the inner anonymous function, where due to the magic of closures the inner (nested) function has access to the index variable defined in the outer function.
setInterval expects a function expresssion/reference as its first parameter, which means it is happy if the above structure is passed in as the first parameter since the above returns a function.
The point of all that is essentially to keep the functionality self-contained, rather than declaring a "plain" function that uses a global variable called "index".
As for what the code within the function actually does, well, it will be executed every 1000ms. If index >= 0, which it will be every time except the first time, then all.eq(index).hide(); will be executed - I assume this hides the element that matches the current index. index is then incremented, with the second if setting it back to 0 if it reaches the maximum number of elements in all, essentially ensuring that the code will keep cycling through the elements. Finally the element at the (newly incremented) index is shown.
Presumably all of these elements are hidden to begin with such that the overall effect is to show and then hide one element at a time, changing once per second.

If index=0, then the all.eq(index).hide() will be executed. The second if could also be executed, but only if all.length = 0.

Related

Store iteration count and then access it outside of for loop in javascript

I expect I'm misunderstanding how hoisting works but I'm confused by why returning i++ is not being hoisted up to the initial declaration of var i. For context I'm trying to create previous/next buttons that utilize scrollIntoView() to cycle between divs.
I have it working as intended for the next button (click and it cycles to next div) but the problem is I can't seem to store the current value of i outside the for loop so that I can use stepsTotal[i] inside another function (for handling the previous button on click).
Here is the code I have working for the next button (previous button function not included). I'm trying to get console.log(i) to print 0,1,2,3, etc. when the next button is clicked.)
const stepsTotal = document.getElementsByClassName("question-steps");
const currentStep = (function() {
var i;
console.log(i); // How do I have this print the current value of i instead of undefined? (i.e. match the results of the second console.log)
for (i=0; i<stepsTotal.length; i++) {
document.getElementById("next-button").addEventListener("click", scrollToCurrentStep);
function scrollToCurrentStep() {
stepsTotal[i].scrollIntoView(true);
i++;
console.log(i); // I want the first console.log to match this one
return
}
return
}
}());

Array reloop to remove previous set of array data generated

My file works just fine in the first round of loop when i try to rerun the function again. It shows the previous value of the previous loop when i try to use the value to match and after which it shows the correct value. If i run the function again and again, it keeps holding on to the value of the previous generated random value.
for (var i=0; i<9; i++)
{
var ranD = Math.floor(Math.random()*33);
if (mathStar.indexOf(ranD)== -1) {
mathStar.push(ranD);
item[i].innerHTML = mathStar[i];
}
else {
i--;
}
itemVal[i].value = mathStar[i];
}
Substitute using const and let for var within for loop to avoid creating global variables and --i could have unexpected results within the code where i++ is also used in the foor loop.
Is this the first occurrence of "mathStar"?
If this is the first place you're using mathStar, it means it gets created globally and that usually leads to confusion. In this case, take a look at this.
Looking at just this, it seems that you are not resetting your "mathStar" value. This way, any time you run this loop for the nth time, the values you have added to "mathStar" using mathStar.push(...) also occur in the list of values.

Creating arrays on condition in javascript

This has been eating me away. Check CodePen here. I have a function that adds elements to a ruler. Call expose.init(lengthOfPeriod) with an integer to set the length of each period in the ruler. Call expose.addAction(time, team) with an integer and a string == 'HOME' || 'AWAY' to place an action in the ruler.
When the screen is resized, I want something to happen to the elements in the ruler that touch each other (basically collapse into a group).
I have a function 'detectOverlap' that takes 2 params and determines if they are touching or not. I also have a function in the resize event handler that populates an array 'allTouching' with all the elements in the ruler that are touching each other at each resize.
if (actionArray.length > 1) { //don't run if there's only one element in the page
actionArray.reduce(function (prev, next) {
if (detectOverlap(prev, next)) {
if (allTouching.indexOf(prev, next) === -1) allTouching.push(prev, next);
}
return next;
});
If actions are touching each other, I need them to collapse into groups. In order to do this, I need to create an array for each group of actions touching each other. However, I haven't been able to make this happen so far.
Here's some pseudo code:
for (i = 0; i < allTouching.length; i++) {
if (detectOverlap(allTouching[0], alltouching) {
touchingGroup[i] = new Array(allTouching[0], all other elements touched by 0);
do not include any element more than once in touchingGroup[i];
do not include any similar arrays (same elements) in allGroups;
allGroups.push(touchingGroup[i]);
}
}
In short, this would need to loop for all the elements in the allTouching array, and create a new touchingGroup[n] for each new group of actions that touch each other.
This sounds simple in my head, and I'm sure there must be a way to do it without code getting overly complex, but I haven't found it yet.
Any feedback would be appreciated.
It seems your question is only about the grouping, so I will ignore the visualisation aspect and assume that the function detectOverlap is correct.
Then you could create the groups in one for loop. In this snippet I have added simplistic sample data and a mock detectOverlap function that will return true when its two arguments are the same (just for the purpose of the snippet):
// Simplistic mock data and function, just to make the snippet work
var actionArray = [1, 1, 3, 3, 3, 8, 9];
function detectOverlap(a, b) { return a === b; }
// Actual code:
var allGroups = [];
var start = 0;
for (var i = 1; i <= actionArray.length; i++) {
if (i >= actionArray.length || !detectOverlap(actionArray[i-1], actionArray[i])) {
if (i - start > 1) // Remove this condition if you want singletons as well
allGroups.push(actionArray.slice(start, i));
start = i;
}
}
console.log(JSON.stringify(allGroups));
Explanation
The variable start is used as an index in the array, from where the most recently found group should start. That group is not yet actually created, since we do not know where it ends, and so I will call this the "undecided" group. start is initialised at 0 to indicate that the first group will start there.
The loop iterates over the array, but starting at 1. In each iteration it decides whether the "undecided" group (started at start) is complete. The group is considered complete when there is no overlap between the previous and the current element of the array. In that case the previous element is the last element of the "undecided" group. The elements for that group are copied from the array with slice. Note that the second argument of slice is the index of the first element that should not be part of the group. Now that group is stored, and start is put at the current index, where the next (and only) "undecided" group should start.
But as long as the two elements do overlap, no new group should be created (that is why the condition has a !). Instead start remains unchanged, and so this "undecided", "unclosed" group is getting bigger in size.
There is an if just before that slice, which prevents the creation of groups that only contain one element. If you remove that if, then also single elements will be isolated in their own "singleton" groups.
The loop will go up to and including arrayAction.length: this is unusual, since that makes the last i an invalid index. But it is useful, since in that case we still want to finish up the last group that is still "ongoing". So in that case i >= arrayAction.length will be true, and so the detectOverlap function will not be called (because the if condition is already known to be true). The if block will be entered and the final group will be created.

Changing Array causing glitches with splice

I have a form event which takes entered values and constructs an array out of them using the following:
keywordArray = []
var getVal = $('#search').val();
if(getVal.length > 1){
keywordArray.push(getVal);
$('.test').on('click', function(){
removeTag(this, getVal);
});
$('#search').val("");
}
My remove function then looks as follows:
function removeTag(el, getVal){
var index = keywordArray.indexOf(getVal);
keywordArray.splice(index, 1);
}
There is no problem when removing a value for the first time as the index is in sync, but once a value is removed the index changes and it seems that JS isn't staying in sync with the updated index, so when I remove another value it glitches with a -1 on splice and will remove all values.
I see two problems: Your removeTag function and the way it is called.
First of all, removeTag() does not need the element, so remove the el parameter if it is really not needed. Second, it should do nothing when the given string does not occur in the array:
function removeTag(val){
var index = keywordArray.indexOf(val);
if (index >= 0) keywordArray.splice(index, 1);
}
The second problem might be related to this part of your code:
$('.test').on('click', function(){
removeTag(this, getVal);
});
I suspect that you don't want to register a click handler for all '.test' elements because you refer to this in the next line but this is not used in the removeTag function (see above). In case there are several '.test' elements, you should make sure that removeTag() is only called with the value that corresponds to the clicked '.test' element. Currently you call removeTag() several times for each click -- once for each '.test' element.
The main problem is pointed out by #vijayP in the comments. Here is a first possible solution.
When adding a tag, use the data attribute to associate the tag with the remove button (el):
function addTag(el, value) {
$(el).data('value', value).on('click', removeTag);
}
I assume you have a separate remove button for each tag. Then, the click handler function can look like this (with the check for a correct index):
function removeTag(){
var index = keywordArray.indexOf($(this).data('value'));
if (index > -1) keywordArray.splice(index, 1);
}

Javascript - passing index to setInterval callback

The problem
I'm trying to simplify a long javascript code and i have a problem with identifying callbacks.
I have a large array with elements to animate on page
[selector, activate interval, hide after]:
things_to_move = [
['.ufo, .chefoven, .sushi', 9900, 2000],
['.hotdog,.pizzaman,.boyballon', 12090, 3600],
(...)
]
Basically, the aim is to to activate each of the selectors every x seconds, and hide them x seconds later, as per the example above.
Current code
After many tries, I ended up with this:
// Activate the element, and set timeout to hide it
var showFun = function(index1) {
$(things_to_move[index1][0]).addClass('move');
setTimeout( function(){hideFun(index1)},things_to_move[index1][2]);
}
// Hide the element
var hideFun = function(index2) {
$(things_to_move[index2][0]).removeClass('move');
}
// Loop through all items and set the interval for each one
for(_A=0; _A < things_to_move.length; _A++) {
setInterval(function(){showFun(_A)}, things_to_move[_A][1]);
}
But of course this doesn't work. Every time the showFun function is called, it takes the value of _A after the loop finished and not the value at which setInterval was set.
Question
So the question is, how can i pass a unique index into the setInterval callback, so the callback knows which array item to use?
Final solution
If anyone is interested, the final solution: Fiddle
The most direct way to solve it is using closures.
Try something like this:
for(_A=0; _A < things_to_move.length; _A++) {
setInterval((function(_innerA){
return function(){ showFun(_innerA); };
})(_A), things_to_move[_A][1]);
}

Categories

Resources