Javascript concept for variable scope [duplicate] - javascript

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
adding 'click' event listeners in loop [duplicate]
(5 answers)
Closed 7 years ago.
I have 5 buttons and I can not change DOM and can not use id and name attribute and cant use innerHTML and now I need to give output like when I click button 1 it should give a alert like "You click button # 1" and same will continue for all 5 buttons. I want to print the value of i .Now I got stuck as I have two solutions :
HTML Part:
//creating 5 buttons
<button>one</button>
<button>two</button>
<button>three</button>
<button>four</button>
<button>five</button>
1st Solution:
/* here I am always having you clicked element #5*/
var nodes = document.getElementsByTagName('button');//getting references
var counter;
for (var i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', function() {
alert('You clicked element #' + i);
});
} //which is not working
2nd Solution:
/* here I am getting correct output*/
var nodes = document.getElementsByTagName('button');
var counter;
for (var i = 0; i < nodes.length; i++) {
assignEvent(nodes[i], i);
}
function assignEvent(node, i){
node.addEventListener('click', function(){
alert('You clicked element#' + (i+1));
});
}//this one is working
Now my question is why Solution 2 is working but solution 1 doesn't.. Please help. Why solution 2 is carrying the value of i correctly, while not solution 1.

If you don't want to create an additional function, just wrap the event listener assignment within a self-invoking anonymous function to which you pass 'i':
var nodes = document.getElementsByTagName('button');
var counter;
for (var i = 0; i < nodes.length; i++) {
(function(j) {
nodes[i].addEventListener('click', function() {
alert('You clicked element #' + (j+1));
});
})(i);
}
jsfiddle here: http://jsfiddle.net/vu31aa7y/

In your first solution when you add the anonymous function as parameter of addEventListener, the alert message is looking for the last value of i in this case i=5, that happens because you don't have the variable as local in the function, so your onlye possible value if the global variable.
If you want to do that try this:
var nodes = document.getElementsByTagName('button');//getting references
function alertMessage(i){
alert('You clicked element#' + (i+1));
}
for (i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', alertMessage.bind(null,i) , false);
}
Also I recommend you this lecture

You can write this:
[].forEach.call(document.getElementsByTagName('button'),
function(elt, index) {
elt.addEventListener('click', function() {
alert('You clicked element #' + (index + 1));
}, false);
});

Related

index number for the line time using JavaScript

I am using the below code in the google tag manager custom JavaScript variable, but it returns same index value for every line item, what can be the issue?
Web page link: https://www.amity.edu/programe-list.aspx?fd=all
function() {
var elements = document.querySelectorAll('.staff-container');
for (var i = 0; i < elements.length; i++){
(function(index){
elements[i].children[0].children[0].addEventListener("click", myScript);
function myScript(){
return("Clicked : ",index);
}
})(i);
}
}
There is an error in the 5th line.
It should be elements[index].children... in that case.
The updated code:
function() {
var elements = document.querySelectorAll('.staff-container');
for (var i = 0; i < elements.length; i++){
(function(index){
elements[index].children[0].children[0].addEventListener("click", myScript);
function myScript(){
return("Clicked : ",index);
}
})(i);
}
}
Here is an alternative way from Simo's blog
Blog link
Although the post is say about visibility element. I test it with click on my website.
This might work
function() {
var list = document.querySelectorAll('.staff-container a'),
el = {{Click Element}};
return [].indexOf.call(list, el) + 1;
}
If it is not working, you might need to provide the screenshot about the click element from your GTM preview.

Vanilla JavaScript bind a mouseout event [duplicate]

This question already has answers here:
Add event handler to HTML element using javascript
(3 answers)
JS li tag onclick not working on IE8
(1 answer)
Correct usage of addEventListener() / attachEvent()?
(2 answers)
Closed 5 years ago.
NO JQUERY!!!!!!
I need to write this in pure JS. I have a bunch of elements in a calendarr timeline, some are linked by invoice_id so I want to add a hover class to all the matching class elements when the mouse is hovered over one, and then remove the hover class on mouse out.
I have the on mouse over part all working fine, but am struggling to get the mouse out event to work.
here is my JS:
function highlightRange(id)
{
var d = document.getElementsByClassName('invoiceCell' + id); // get all the elements
d.className += "hover"; // add the hover class
for (var i = 0; i < d.length; i++) { // remove the hidden class
d[i].classList.remove('hidden');
}
// now how to I bind the mouseout event??
}
I have a similar script running jQuery that does exactly this, but in this scenario I cannot use jQuery.
The jQuery version looks like this:
function highlightRange(id)
{
$(".price_cell_"+id).addClass('hover');
$(this).bind("mouseout", function(){
$(".price_cell_"+id).removeClass('hover');
})
}
EDITED after response:
function highlightRange(id)
{
var d = document.getElementsByClassName('invoiceCell' + id); // get all the elements
d.className += "hover"; // add the hover class
for (var i = 0; i < d.length; i++) // remove the hidden class
{
d[i].classList.remove('hidden');
d[i].on('mouseout', function(){
d[i].classList.remove('hover');
d[i].className += "hidden";
});
}
}
Is that what you're looking for?
object.addEventListener("mouseout", function(){
...
});

in javascript, function in loop can't access the loop scope variable [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
it is based on jQuery Easy UI
Assume that here is the HTML code
<div id="menu" class="easyui-menu" style="width:120px;">
<div>New</div>
<div>
<span>Open</span>
<div style="width:150px;">
<div><b>Word</b></div>
<div>Excel</div>
<div>PowerPoint</div>
</div>
</div>
<div data-options="iconCls:'icon-save'">Save</div>
<div class="menu-sep"></div>
<div>Exit</div>
</div>
The problem is if you setup the menu in the loop, the function always triggered by the last statement, i.e. i always equals to 4 in console.log(itemLabel + i + "is selected.");
$(function(){
var jqMenu = $("#menu");
for (var i = 0; i < 5; i++) {
var itemLabel = "item " + i;
// correct
var onclickFunction = function(){
console.log(itemLabel + i + "is selected.");
}
jqMenu.menu("appendItem",{
"text":itemLabel,
"onclick": function(){
console.log(itemLabel + " is selected.");
}
});
}
})
$(document).bind('contextmenu',function(e){
e.preventDefault();
$("#menu").menu('show', {
left: 200,
top: 100
});
});
the problem you are facing above can be solved using closures. I had this challenge once and solved it using closures.
Change your code to match this. Try using this format in your code.
for (var i = 0; i < 5; i++) {
(function(x){
console.log(x);
})(i);
}
Yeah, use a function to create a closure would help, also check out this answer: https://stackoverflow.com/a/3572616/883571
Personally I would suggest using forEach to bypass the problem:
[1,2,3,4,5].forEach(function(i) {
console.log(i)
});

Looping inside jQuery function only issue

I'm having some trouble with jQuery in Meteor - I'm just trying to learn so I hope someone could help.
So when #addButton is clicked it will append the div to the .formField and each div created on click will have an unique class, eg formField[1], formField[2] etc
The trouble is when the button is clicked instead of just changing the name of the div only, the div is also added 50 times. I know how dumb it sounds as its a loop, but how would I loop only the div's class on click so each have a different name?
My code is below:
Template.form.events({
'click #addButton': function(event) {
var i;
for (i = 0; i < 50; i++) {
$(".formField").append('<div class="formField['+i+']">.....</div>');
}
return false;
If I understand what you are doing here you don't need a loop. You just need a variable to increment every time the button is clicked. Take your append out of the loop and instead on click increment your variable by one then call an append. No loop necessary.
var i = 0;
Template.form.events({
'click #addButton': function(event) {
i += 1;
$(".formField").append('<div class="formField['+i+']">.....</div>');
}
});
return false;
Do it like this, (i.e. by creating a closure), click run to verify
var uuid = 0;
$('#addButton').on('click', function (event) {
uuid = uuid + 1;
$(".formField").append('<div class="formField[' + uuid + ']">Form' + uuid + '</div>');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="formField"></div>
<input type="button" value="Add New" id="addButton"></input>

How can I show/change array contents?

I'm doing the quiz from javascriptissexy.
Dynamically (with document.getElementById or jQuery) remove the current question and add the next question, when the user clicks the “Next” button. The Next button will be the only button to navigate this version of the quiz.
How can I remove the current question + list of answers and display the new ones dynamically? It works fine right now but think it's asking me to make the transition from questions smoother.
function nextQuestion(){
j++;
currentQuestion.innerHTML = allQuestions[j].question;
for(var i = 0; i < allQuestions[j].choices.length; i++){
currentChoices[i].innerHTML = allQuestions[j].choices[i];
};
};
Before replacing the content, you must to hide, change the content and then show-it again
$("#question").fadeOut("slow", function(){
$("#question_content").html(allQuestions[question].question);
for(var i = 1; i <= allQuestions[question].choices.length; i++){
$("#choise"+i).html(allQuestions[question].choices[i-1]);
};
$("#question").fadeIn("slow");
});
Please see this example on JSFiddle http://jsfiddle.net/en4rt9o7/
You can remove using the shift
function nextQuestion() {
var question = allQuestions.shift();
currentQuestion.innerHTML = question.question;
for (var i = 0; i < question.choices.length; i++) {
currentChoices[i].innerHTML = question.choices[i];
};
};

Categories