The setTimeout does not keep the variable these according to the value it contained at the time it was called. It will always pick up the current reference. At the end of the first ten seconds, the loop each is already over and the reference in these will be the last element. And it will be so for all other times that the setTimeout execute.
count = 1;
$("a.btns").each(function() {
if ($(this).parent("span").attr("id") == "btn_default") {
return;
}
count++;
these = $(this);
setTimeout(function() {
$(these).click();
}, 1000 * count);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span>
<a class="btns" onclick="console.log(1)"></a>
</span>
<span>
<a class="btns" onclick="console.log(2)"></a>
</span>
<span>
<a class="btns" onclick="console.log(3)"></a>
</span>
This is a common problem that happens when we don’t understand what the closures in Javascript and how it works with the scopes/contexts of functions.
The solution would be to find a way to keep the reference to the button element when calling the function inside the setTimeout. You can play your setTimeout for a parameterized auxiliary function (maintaining these in a different context, unrelated to the context of the function in which it was called):
count = 1;
$("a.btns").each(function() {
if ($(this).parent("span").attr("id") == "btn_default") {
return;
}
count++;
these = $(this);
clickTimeout(these, 1000 * count);
});
function clickTimeout(el, time){
setTimeout(function() {
$(el).click();
}, time);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span>
<a class="btns" onclick="console.log(1)"></a>
</span>
<span>
<a class="btns" onclick="console.log(2)"></a>
</span>
<span>
<a class="btns" onclick="console.log(3)"></a>
</span>
Or you can use the method bind() Javascript to change the context of this within the function being passed to the setTimeout. We can pass the variable these as the new this function and use it in there without losing your reference:
count = 1;
$("a.btns").each(function() {
if ($(this).parent("span").attr("id") == "btn_default") {
return;
}
count++;
these = $(this);
setTimeout((function(el) {
this.click();
}).bind(these), 1000 * count);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span>
<a class="btns" onclick="console.log(1)"></a>
</span>
<span>
<a class="btns" onclick="console.log(2)"></a>
</span>
<span>
<a class="btns" onclick="console.log(3)"></a>
</span>
I hope I’ve helped.
EDITION
In Javascript ES6 were inserted the variables of type Let and const. They add an interesting feature called Temporal Dead Zones, which allows them to retain the value they contained in the scope when they were executed. It’s a little hard to understand concept.
In practice, his each works as expected with only a small change in the original code:
count = 1;
$("a.btns").each(function() {
if ($(this).parent("span").attr("id") == "btn_default") {
return;
}
count++;
const these = $(this); // <--- Agora utiliza "const"
setTimeout(function() {
$(these).click();
}, 1000 * count);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span>
<a class="btns" onclick="console.log(1)"></a>
</span>
<span>
<a class="btns" onclick="console.log(2)"></a>
</span>
<span>
<a class="btns" onclick="console.log(3)"></a>
</span>
Note that now the variable these is declared using const. This way, your code becomes more intuitive and easy to understand, without having to break it (and thus affect readability) to take into account the and the contexts of this.
Finally, we have yet another way to solve your problem by using Arrow functions, which are also a new Javascript functionality ES6:
count = 1;
$("a.btns").each(function() {
if ($(this).parent("span").attr("id") == "btn_default") {
return;
}
count++;
setTimeout(() => $(this).click(), 1000 * count);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span>
<a class="btns" onclick="console.log(1)"></a>
</span>
<span>
<a class="btns" onclick="console.log(2)"></a>
</span>
<span>
<a class="btns" onclick="console.log(3)"></a>
</span>
As Arrow functions do not have their own context this, and therefore use the this of the lexical context that is just above. This means that when your Arrow Function use the $(this), she will be accessing the object this of the iteration of each in which it was called, also retaining the reference of the button element.
The
setTimeoutdoes not maintain thetheseaccording to the value it contained at the time it was called. It will always pick up the current reference. At the end of the first ten seconds, the loopeachis already over and the reference inthesewill be the last element. And it will be so for all other times that thesetTimeoutexecute.– Pedro Corso