javascript closure not working as it should - javascript

see the first code:
var count = 0;
(function addLinks() {
var count = 0;//this count var is increasing
for (var i = 0, link; i < 5; i++) {
link = document.createElement("a");
link.innerHTML = "Link " + i;
link.onclick = function () {
count++;
alert(count);
};
document.body.appendChild(link);
}
})();
When the link gets clicked the counter variable keeps on increasing for each link element. This is the expected result.
Second:
var count = 0;
$("p").each(function () {
var $thisParagraph = $(this);
var count = 0;//this count var is increasing too.so what is different between them .They both are declared within the scope in which closure was declared
$thisParagraph.click(function () {
count++;
$thisParagraph.find("span").text('clicks: ' + count);
$thisParagraph.toggleClass("highlight", count % 3 == 0);
});
});
Here the closure function is not working as expected. On each click on the paragraph element, the counter var is increased but that increment is not displayed on click on second paragraph element? What is the reason for this? Why is this happening? The count variable is not increasing for each paragraph element.

do you mean:
var count = 0;
$("p").each(function() {
var $thisParagraph = $(this);
//var count = 0; //removed this count, as it re-inits count to 0
$thisParagraph.click(function() {
count++;
$thisParagraph.find("span").text('clicks: ' + count);
$thisParagraph.toggleClass("highlight", count % 3 == 0);
});
});

Related

Check if Element value has populated during for loop

I'm trying to loop through a table with the id mytab1 and all the td's with myfindclass2.
The values are being populated by another API so it could take up to a few seconds for the values to be available.
I'm using the setinterval but it doesn't seem to work in a for loop.
When ran it will not wait till the element is populated it just finds the only one populated then continuously runs the nextstep() function as if the clearInterval never worked. And just skips over the elements that are null/''
Is there way to write the setinterval/ check if the element is populated as a function outside of the for loop?
**I'm trying to use the most basic of JS as this will be used in all browsers.
function refreshGauge() {
var table = document.getElementById("mytab1");
var targetTDs = table.querySelectorAll(".myfindclass2");
for (var i = 0; i < targetTDs.length; i++) {
var td = targetTDs[i];
var fvals = td.querySelectorAll("[id*='calcValue']");
var fval = fvals[0].innerText.replace('%', '');
var checkElem = "#" + fvals[0].getAttribute('id');
console.log(checkElem + '-' + $(checkElem).length)
var checkExist = setInterval(function() {
if (fval === '') {
console.log("Doesn't Exist Yet!");
//Update variables to see if updated
fvals = td.querySelectorAll("[id*='calcValue']");
fval = fvals[0].innerText.replace('%', '');
} else {
//stop interval
clearInterval(checkExist);
//perform next function : creates gauge
nextstep(td);
}
}, 100); // check every 100ms
}
console.log('done')
};
my work around is :
function refreshGauge() {
var table = document.getElementById("mytab1");
var targetTDs = table.querySelectorAll(".myfindclass2");
for (var i = 0; i < targetTDs.length; i++) {
var td = targetTDs[i];
var fvals = td.querySelectorAll("[id*='calcValue']");
var fval = fvals[0].innerText.replace('%','');
var checkElem = "#" + fvals[0].getAttribute('id');
console.log(checkElem + '-' + $(checkElem).length)
nextstep(td)
}
console.log('done')
} ;
var timesRun = 0;
var interval = setInterval(function(){
timesRun += 1;
if(timesRun === 10){
clearInterval(interval);
}
refreshGauge()
}, 2000);
but would like to check when the values populated so I don't have to re-run the function 10+ times

How to exchange counter after delete field using plain javascript

I'm new in javascript. i have a JS function that add and remove input fields. its working fine with my JS function. But I want when delete a field its Id looks like:
I have
no. 1
no. 2
no. 3
After Delete 2:
no. 1
no. 2
already i got this answer:
Reset JavaScript Counter after Deleting a field
But i want it with plain javascript. Can anyone help?
<script>
var count = 1;
function add_new(){
count++;
var div1 = document.createElement('div');
div1.id = count;
var delLink = '<button type="button" onclick="deleteLink('+count+')" class="btn btn-primary">Delete</button>';
div1.innerHTML = document.getElementById('add_link1').innerHTML+delLink;
document.getElementById('add_link').appendChild(div1);
document.getElementById("input_link1").id = count;
document.getElementById("input_link2").id = count;
document.getElementById("input_link3").id = count;
}
function deleteLink(eleId){
var ele = document.getElementById(eleId);
var par = document.getElementById('add_link');
par.removeChild(ele);
}
</script>
After deleting an element call the following function to reset Id of existing elements and also reduce the count.
function reset_counter(deletedCount) {
for (var impactedElementId = deletedCount + 1; impactedElementId < count; impactedElementId++) {
var currentElement = document.getElementById(impactedElementId);
currentElement.id = impactedElementId - 1;
var button = currentElement.firstChild;
button.innerHTML = 'Delete ' + currentElement.id;
button.setAttribute('onclick', 'deleteLink(' + currentElement.id + ')');
}
count--;
}
The full code is available here: AddDeleteElements Sample Code

How to call two functions with the same event?

I want to display eight elements of first array in eight divs with class .box, and one element of second array in the center div with id center by clicking any div except the middle one with id center. The problem is the two functions named nextElems & arr are not called simultaneously. If one function is called, another does not work. I am able to call the function arr, but how to call the function nextElems simultaneously on the click of any div.
Your suggestions are always really great and helpful and I am much obliged.
<div id="container"> </div>
var words = [40];
// Showing 8 elements each time in different divs
var count = 0;
var x = "";
function nextElems() {
var newArray = words.slice(count, count + 8);
for (i = 0; i < newArray.length; i++)
{
x += '<div class=box>' + newArray[i] + '</div>';
document.getElementById('container').innerHTML = x;
}
x = "";
count += 8;
// Creating div in middle between fourth and fifth div
if (count <= 40) {
var center = document.querySelector('#div-3');
center.insertAdjacentHTML('afterend', '<div id="center"> </div>')
}
}
nextElems();
// Showing elements one at a time in order
var oppo = ["White", "Easy", "Soft", "Low", "Good"];
var x = "";
var count = 0;
function arr() {
if (count < oppo.length) {
x += '<div>' + oppo[count] + '</div>';
document.getElementById('center').innerHTML = x;
count++;
} else {
count = 0;
document.getElementById('center').innerHTML = "";
}
x = "";
}
arr();
$('.box').click(function() {
arr();
});
I have tried
$('.box').click(function() {
arr();
nextElems();
});
I have also tried it on the click of a button & with other jQuery
methods.
You need to declare variables inside a function scope.
Because now your code performed in the next order:
var count = 0;
var x = "";
nextElems();
var oppo = ["White", "Easy", "Soft", "Low", "Good"];
var x = "";
var count = 0;
..
someday later ...
..
arr(); //here you modify your global variables "x" and "count", so next function will use it with unpredictable values
nextElems();
so you need to declare it like
function nextElems() {
var count = 0;
var x = "";
...somecode
}
and then you can use
$('.box').click(function() {
arr();
nextElems();
});

How scope works in a for loop?

When I created 5 buttons in a loop and click the value of i is always 6
function createButtons() {
for (var i = 1; i <= 5; i++) {
var body = document.getElementsByTagName("BODY")[0];
var button = document.createElement("BUTTON");
button.innerHTML = 'Button ' + i;
(function(num) {
button.onclick = function() {
alert('This is button ' + num);
}
})(i);
body.appendChild(button);
}
}
but when I change the scope of i to block scope(using IIFE or let keyword) it gives the right value of i. How does it working under the hood of javascript?
I have seperated your functions into incorrect and correct one.
The incorrect version is what you're asking. The correct version is what you've already figured out but don't know why it work.
In the incorrect version, the value of i will always be updated to the most recent value because i belongs to the createButtons function and is shared with all onclick handler, and it is changing with the loop.
In the correct version, the value of i is given to the IIFE as num, and num belongs to the IIFE and not to createButtons.
Because of that, num is fixed because a new num is created for every loop thus is not shared with the other onclick handler.
Why? It is how closure works in JavaScript.
Read this for deeper understanding on JavaScript closure.
function createButtons_incorrect() {
for (var i = 1; i <= 5; i++) {
var body = document.getElementsByTagName("BODY")[0];
var button = document.createElement("BUTTON");
button.innerHTML = 'Bad ' + i;
button.onclick = function() {
alert('This is button ' + i);
}
body.appendChild(button);
}
}
function createButtons_correct() {
for (var i = 1; i <= 5; i++) {
var body = document.getElementsByTagName("BODY")[0];
var button = document.createElement("BUTTON");
button.innerHTML = 'Good ' + i;
(function(num){
button.onclick = function() {
alert('This is button ' + num);
}
})(i);
body.appendChild(button);
}
}
createButtons_incorrect();
document.body.appendChild(document.createElement('br'));
createButtons_correct();

To Do List with priorities in JavaScript

I am working on a T- Do list in JavaScript. It adds a new task to the list with delete and complete buttons. When the complete button is clicked, the task will be marked in red. There is also a tasks counter on the top and a remove all tasks button on the bottom. All of these features work fine. But I would like the tasks to be added based on the priority given. Priority should be an integer between 1 and 10, where 10 is top priority and 1 is least. Unfortunately, the code I wrote for that part doesn't seem to be working. I added a new variable called index that stores info where the new item should be added. Can you help me find out which part of the code has to be changed? Here it is:
document.addEventListener("DOMContentLoaded", function () {
var add = document.getElementById("addTaskButton");
var removeFinished = document.getElementById("removeFinishedTasksButton");
var task = document.getElementById("taskInput");
var list = document.getElementById("taskList");
var body = document.querySelector("body");
var prior = document.getElementById("taskPriority");
//To do counter
var toDo = document.createElement("span");
body.insertBefore(toDo, list);
var allTasks = document.querySelectorAll("li");
var counter = allTasks.length;
toDo.innerHTML = "Tasks to do: " + counter;
//Add task
add.addEventListener("click", function () {
if (task.value.length >= 5 &&
task.value.length <= 100 &&
prior.value > 0 &&
prior.value <= 10) {
//Add task to the list
var newTask = document.createElement("li");
newTask.dataset.priority = prior.value;
var all = document.querySelectorAll("li");
for (var i = 0; i < all.length; i++) {
var index = all.length;
if (parseInt(newTask.dataset.priority) < parseInt(all[i].dataset.priority)) {
index = i;
break;
}
}
list.insertBefore(newTask, list[index]);
var taskName = document.createElement("h1");
newTask.appendChild(taskName);
taskName.innerHTML = task.value;
//Add delete button
var delBtn = document.createElement("button");
newTask.appendChild(delBtn);
delBtn.innerHTML = "Delete";
delBtn.classList.add("delete");
//Add complete button
var complBtn = document.createElement("button");
newTask.appendChild(complBtn);
complBtn.innerHTML = "Complete";
complBtn.classList.add("complete");
counter++;
toDo.innerHTML = "Tasks to do: " + counter;
//Mark completed in red and adjust counter
complBtn.addEventListener("click", function () {
if (this.parentElement.style.color === "") {
this.parentElement.style.color = "red";
this.parentElement.setAttribute("done", "yes");
counter--;
toDo.innerHTML = "Tasks to do: " + counter;
} else if (this.parentElement.style.color === "red") {
this.parentElement.style.color = "";
this.parentElement.removeAttribute("done");
counter++;
toDo.innerHTML = "Tasks to do: " + counter;
}
});
//Delete selected item and adjust counter
delBtn.addEventListener("click", function () {
this.parentElement.parentNode.removeChild(this.parentElement);
counter--;
toDo.innerHTML = "Tasks to do: " + counter;
});
task.value = "";
prior.value = "";
} else {
event.preventDefault();
alert("Task should have from 5 to 100 characters. Priority should be an integer between 1 and 10");
}
});
//Remove completed items
removeFinished.addEventListener("click", function () {
var tasks = document.querySelectorAll("li");
for (var i = 0; i < tasks.length; i++) {
if (tasks[i].hasAttribute("done")) {
tasks[i].parentNode.removeChild(tasks[i]);
}
}
});
});
And here is the HTML:
<body>
<input id="taskInput" placeholder="Place your task here"><br>
<input id="taskPriority" placeholder="Place task priority (1-10)"><br>
<button id="addTaskButton">Add task</button>
<ul id="taskList">
</ul>
<button id="removeFinishedTasksButton">Remove finished tasks</button>
</body>
First thing is that index was being declared inside a loop so it could not be used outside of it. You need to declare index and the default value for index outisde of the loop for your function to work.
Second thing was that the value for list was never getting updating after the document was loaded for the first time. You need to use the all array to find the proper element to insert before, not list. list was assigned outside of your event handler so it did not have fresh data, also it is not a collection of li elements which is what you needed in this case.
document.addEventListener("DOMContentLoaded", function () {
var add = document.getElementById("addTaskButton");
var removeFinished = document.getElementById("removeFinishedTasksButton");
var task = document.getElementById("taskInput");
var list = document.getElementById("taskList");
var body = document.querySelector("body");
var prior = document.getElementById("taskPriority");
//To do counter
var toDo = document.createElement("span");
body.insertBefore(toDo, list);
var allTasks = document.querySelectorAll("li");
var counter = allTasks.length;
toDo.innerHTML = "Tasks to do: " + counter;
//Add task
add.addEventListener("click", function () {
if (task.value.length >= 5 &&
task.value.length <= 100 &&
prior.value > 0 &&
prior.value <= 10) {
//Add task to the list
var newTask = document.createElement("li");
newTask.dataset.priority = prior.value;
var all = document.querySelectorAll("li");
var index = all.length;
for (var i = 0; i < all.length; i++) {
if (parseInt(newTask.dataset.priority) < parseInt(all[i].dataset.priority)) {
index = i;
break;
}
}
list.insertBefore(newTask, all[index]);
var taskName = document.createElement("h1");
newTask.appendChild(taskName);
taskName.innerHTML = task.value;
//Add delete button
var delBtn = document.createElement("button");
newTask.appendChild(delBtn);
delBtn.innerHTML = "Delete";
delBtn.classList.add("delete");
//Add complete button
var complBtn = document.createElement("button");
newTask.appendChild(complBtn);
complBtn.innerHTML = "Complete";
complBtn.classList.add("complete");
counter++;
toDo.innerHTML = "Tasks to do: " + counter;
//Mark completed in red and adjust counter
complBtn.addEventListener("click", function () {
if (this.parentElement.style.color === "") {
this.parentElement.style.color = "red";
this.parentElement.setAttribute("done", "yes");
counter--;
toDo.innerHTML = "Tasks to do: " + counter;
} else if (this.parentElement.style.color === "red") {
this.parentElement.style.color = "";
this.parentElement.removeAttribute("done");
counter++;
toDo.innerHTML = "Tasks to do: " + counter;
}
});
//Delete selected item and adjust counter
delBtn.addEventListener("click", function () {
this.parentElement.parentNode.removeChild(this.parentElement);
counter--;
toDo.innerHTML = "Tasks to do: " + counter;
});
task.value = "";
prior.value = "";
} else {
event.preventDefault();
alert("Task should have from 5 to 100 characters. Priority should be an integer between 1 and 10");
}
});
//Remove completed items
removeFinished.addEventListener("click", function () {
var tasks = document.querySelectorAll("li");
for (var i = 0; i < tasks.length; i++) {
if (tasks[i].hasAttribute("done")) {
tasks[i].parentNode.removeChild(tasks[i]);
}
}
});
});
<body>
<input id="taskInput" placeholder="Place your task here"><br>
<input id="taskPriority" placeholder="Place task priority (1-10)"><br>
<button id="addTaskButton">Add task</button>
<ul id="taskList">
</ul>
<button id="removeFinishedTasksButton">Remove finished tasks</button>
</body>

Categories

Resources