Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I'm trying to iterate over an array and apply an onclick event to each item. I'm hoping to be able to click each of my divs and have them console log their value. Right now I'm stuck on how to apply the onclick to each div. I'm new to JS, so I'm not completely of why I shouldn't make a function inside of a loop like JSBin is complaining about. I've messed around with a lot of different ways to do this, but am truly stuck...
JSBin
function numberTrack() {
var gridItems = document.getElementsByClassName("grid");
for (var i = 0; i < gridItems[0].length; i ++) {
gridItems.onclick = function(){
alert("hello");
};
}
}
numberTrack();
var c = document.getElementsByClassName("divs"); <--- array of divs
for (var i = 0; i < c.length; i++) {
c[i].onclick = function() {
console.log(this.value);
}
}
JSBin is complaining because it wants you to declare the function outside the for loop and then assign it inside the for loop. This is more efficient then what you are currently doing, which is assigning a new anonymous function to each item in the array. All those identical functions will have to be created and stored separately in memory.
You can do something like this instead:
function alertHello() {
alert("hello");
}
for (var i = 0; i < gridItems.length; i++) {
gridItems[i].onclick = alertHello;
}
You need to loop through all the items in the gridItems collection, And inside the loop, get each item using the iterator i value.
function handleClick()
{
alert("hello");
}
function numberTrack() {
var gridItems = document.getElementsByClassName("grid");
for (var i = 0; i < gridItems.length; i ++) {
gridItems[i].onclick = handleClick;
}
}
numberTrack();
If you are allowed to use jQuery, you can bind the event to items like this.
$(function(){
$(document).on("click",".grid",function(e){
var item =$(this);
alert(item.html())
})
})
Here is a working jsBin sample
Related
In the following code i'm trying to get subquestions list for each question and show them in the view ,but i have problem in updating subquestion for each question,it don't update so I get same subquestion list for all question.
How can I show each question and list all subquestion under it?
function loadAllQuestionGroup () {
Questiongroup.query({}).$promise.then(function(group){
vm.questiongroups = group;
for(var i=0;i<group.length;i++){
var grouptitle=group[i].title
Question.questionsByQuestionGroup({id:group[i].id}).$promise.then(function(question){
vm.question = question;
for(var j=0;j<question.length;j++){
Subquestion.subquestionsByQuestion({id:question[j].id}).$promise.then(function(subquestion){
vm.subquestions=subquestion;
});
}
});
}
});
}
<div ng-repeat="question in vm.question">
{{question.name}}
<div ng-repeat="subquestion in vm.subquestions">
{{subquestion.name}}
</div>
</div>
Looks like it's not an issue related with angular scope, it's pure javascript scope that you have trouble with. If you making asynchronous calls in the loop, you will receive each time the last element. To fix it, you should wrap promises inside loop with anonymous functions.
Check this classic example(you can think that setTimeout function is equivalent of your Question.questionsByQuestionGroup({id:group[i].id}).$promise, they are both async functions)
//THIS IS EQUIVALENT TO YOUR CASE
//In this case it will return each time last element, 5 in this case
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i)
});
}
//Result: 5 5 5 5 5
//THIS IS HOW YOU SHOULD REFACTOR
//Make a wrapper for async, and it will work as needed
for (var j = 0; j < 5; j++) {
(function(index) {
setTimeout(function() {
console.log(index)
});
})(j);
}
//Result: 0 1 2 3 4
P.S. After you will fix it with wrappers, your code will be even more unreadable, better to refactor it to separate functions.
Individual questions need to be pushed to a question list. Create a subquestion list for each individual question. Individual subquestions need to be pushed to their respective lists.
function loadAllQuestionGroup () {
Questiongroup.query({}).$promise.then(function(group){
vm.questiongroups = group;
//Initialize question list
vm.questionList = [];
for (var i=0;i<group.length;i++){
var grouptitle=group[i].title;
Question
.questionsByQuestionGroup({id:group[i].id})
.$promise
.then(function(question){
//Push question to list
vm.questionList.push(question);
for (var j=0;j<question.length;j++) {
//Initialize subquestion list
question.subquestionList = [];
Subquestion
.subquestionsByQuestion({id:question[j].id})
.$promise
.then(function(subquestion){
//Push subquestion to list
question.subquestionList.push(subquestion);
})
;
};
})
;
}
});
}
Subquestion interation (ng-repeat) should be done on the question iterator.
<div ng-repeat="question in vm.questionList">
{{question.name}}
<div ng-repeat="subquestion in question.subquestionList">
{{subquestion.name}}
</div>
</div>
Notice that a subquestionList property was added to each question in the questionList.
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 7 years ago.
Improve this question
I am trying to push a value into an array and it is giving me this error in the developer tools.
Uncaught TypeError: Cannot read property 'push' of null
Here is the code that it seems to be sticking on, word and local word were defined earlier like this.
var word = [];
var localWord = [];
function setLocalArray() {
// first get words from text field and update word array.
word = document.getElementById("words").value.split(',');
// store word array in localStorage
for(var i=0; word.length > i; i++) {
var key2 = "part"+i;
localStorage.setItem(key2,word[i]);
localWord.push(key2);
}
localStorage.setItem("localWord",JSON.stringify(localWord));
text2Array();
reveal();
}
localWord.push(key2); Seems to be what it is getting stuck on. I have looked at everything I can find on the push method and I can't seem to find why it is giving me this error. Help?
Here is the full code at jsfiddle http://jsfiddle.net/runningman24/jnLtpb6y/
Try this...
var localWord = new Array(); //create new array
var word = new Array();
function setLocalArray() {
word = document.getElementById("words").value.split(',');
// store word array in localStorage
for(var i=0; word.length > i; i++) {
var key2 = "part"+i;
localStorage.setItem(key2,word[i]);
localWord.push(key2);
}
}
I found the problem, if you look in the jsfiddle I posted I am trying to pull localWord from localStorage even though it doesn't exist and so it sets it to null. Thank you to all for the ideas and contributions.
You could try isolating the scope of your variable using the module pattern:
var arrayManager = (function () {
var word = [];
var localWord = [];
function setLocalArray() {
// first get words from text field and update word array.
word = document.getElementById("words").value.split(',');
// store word array in localStorage
for(var i=0; word.length > i; i++) {
var key2 = "part"+i;
localStorage.setItem(key2,word[i]);
localWord.push(key2);
}
localStorage.setItem("localWord",JSON.stringify(localWord));
text2Array();
reveal();
}
return {
setLocalArray:setLocalArray
} ;
}());
and the from the outside you have to simply call arrayManager.setLocalArray()
This question already has answers here:
How do JavaScript closures work?
(86 answers)
Looping setTimeout
(3 answers)
Closed 8 years ago.
Please help me fix this code
HTML
<p class="imgclass">Test1</p>
<p class="imgclass">Test2</p>
<p class="imgclass">Test3</p>
JavaScript
imgclassElements = document.getElementsByClassName('imgclass');
for (var i = 0; i < imgclassElements.length; i++){
imgclassElements[i].onmouseover=function(){alert(i);};
}
In browser onmouseover elements with class .imgclass appear alertbox only with text "3". How fix this? I want to pop-up alert window with values appropriate .imgclass elements order.
This is the simplest way to do it, you can use a closure but I'll keep it simple. Also you can access the element and apply styles, or get info from it in the click handler
FIDDLE
http://jsfiddle.net/bz7a2vf5/1/
HTML
<p id="item1" class="imgclass">Test1</p>
<p id="item2" class="imgclass">Test2</p>
<p id="item3" class="imgclass">Test3</p>
JS
imgclassElements = document.getElementsByClassName('imgclass');
for (var i = 0; i < imgclassElements.length; i++) {
click(i);
}
function click(i){
imgclassElements[i].onmouseover = function (e) {
// you can also get the element from e.target
alert(i);
alert(e.target.id);
};
}
This may not be the most elegant way, but
imgclassElements[i].addEventListener('mouseover',function(j){return function(){alert(j);};}(i));
should do it.
That said, you should really read up on JS closures as suggested in the comments to your question.
That problem is occurring because of the time alert is being called, value of i is incremented to 3 that's why it's giving 3 every time.
Try to bind a function with the current value of i like bellow
imgclassElements = document.getElementsByClassName('imgclass');
var printVal = function(i){
return function(){
alert(i);
};
}
for (var i = 0; i < imgclassElements.length; i++){
imgclassElements[i].onmouseover = printVal(i)
}
There can be better solutions also.
The easiest way is to extract the content of your loop in a function
function handleElement(element, i) {
element.onmouseover=function(){alert(i);};
};
for (var i = 0; i < imgclassElements.length; i++){
handleElement(imgclassElements[i], i);
}
Or you can use forEach (Sadly it's a HTMLCollection and not an Array, so it's a bit difficult)
Array.prototype.forEach.call(imgclassElements, function(element, i) {
element.onmouseover=function(){alert(i);};
});
Or give the index as an argument
for (var i = 0; i < imgclassElements.length; i++){
imgclassElements[i].onmouseover=(function(i){alert(i);}).bind(null, i);
}
And always a good thing to learn about closures
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I have the following loop, which loops through required_types array and finds linked objects:
for (var i = 0; i < required_types.length; i++)
{
var linked_objects =linker.linked(required_types[i].type);
}
Now, I want to break the loop once I've found the type which has more than 0 objects linked, so in an accustomed way I'd do it like that:
var has_linked_objects = false;
for (var i = 0; i < required_types.length; i++)
{
var linked_objects = ctx.linker.linked(required_types[i].type);
var has_linked_objects = linked_objects > 0;
if (has_linked_objects)
{
break;
}
}
However, I've recently read interesting chapter from Code Complete which states that exists from a loop should be as clear as possible and be stated in the beginning of the loop. This allows a reader to know all possible exit conditions before even examining the loop. So now I would write the loop in this way:
var has_linked_objects = false;
for (var i = 0; i < required_types.length && has_linked_objects === false; i++)
{
var linked_objects = ctx.linker.linked(required_types[i].type);
var has_linked_objects = linked_objects > 0;
}
Is the second option really a better choice that the first one? If so, why?
IMHO, this is bad advice. The condition could get very long and it no longer matches my mental "for-each" pattern.
I read this
for (var i = 0; i < required_types.length; i++) {
if (has_linked_objects) {
break;
}
}
as
for each element in required_types { // easy
if(found) {
break;
}
}
And I read this
for (var i = 0; i < required_types.length && has_linked_objects === false; i++) {
}
as
Wow, this is a complicated loop! Let me read this carefully...
When does has_linked_objects change?
Could it change to true and change back to false in a single iteration of the loop?
I think it's much easier to think about simple conditions: “if this happens, then break” than to keep track of many variables in the loop body and test everything at once in the next iteration. The second approach could make it harder to write the rest of the function, because you can't be sure which condition triggered the end of the loop (was the element found or not?)
You can use some method like this
if (required_types.some(function(cType){return ctx.linker.linked(cType) > 0;})) {
...
}
This breaks out of iterating the Array at the first iteration which returns true. If I have to choose between the two options you provided, I would choose the second (with the following fixed code)
var has_linked_objects = false;
for (var i = 0; i < required_types.length && has_linked_objects === false; i++)
{
has_linked_objects = ctx.linker.linked(required_types[i].type) > 0;
}
It is right, that you should put all conditions in the head of the loop if possible. If it is not possible or if it would make the code even less readable, you can - of course - use a break. In your case the loop is so small and simple that it probably won't make a huge difference. If you really want to make sure that fellow developers know what's going on, you can still write a comment that lists alternative exit points in the loop.
// Iterates required_types and breaks on an entry with linked objects
for (var i = 0; i < required_types.length; i++)
{
...
if (has_linked_objects)
{
break;
}
}
If you need to further use the last used element in the array, having the second condition in the head of the loop introduces another issue. When the loop ends, i will always be 1 higher than your result. You will have to decrease i by one after the loop. Not that it's complicated to do that, but I, as someone who reads the code, would be confused for a second or two.
for (var i = 0; i < required_types.length && has_linked_objects === false; i++)
{
...
}
i--; // This can easily get overread or misinterpreted
var obj_with_linked_objects = required_types[i];
TL/DR: I personally prefer break in this case.
I'm putting together this script which pulls two child elements from a containing div #mini_ads, adds them to an array. I want to be able to use the array to select them via index in order to manip. them individually.
I know I can just select them w/o even using an array of course, but I want this array as I may add multiple more elements later.
The issue is that I am not able to select the items individually by their index in the array. The current script I've got going is selecting and manipulating both objects in the array as if they're both index[0].
var miniAds = $('#mini_ads');
var elements = miniAds.children();
var changeWtime;
var adsArr = new Array();
var i = 0;
var x = 0;
adsArr.push(elements);
console.log(adsArr);
adsArr[i].css("display", "none");
var changeWtime = setInterval(function () {
for (x; x < 1; x++) {
return x;
while (x > i) {
adsArr[1].css("display", "block");
}
};
}, 5000);
console.log(x);
changeWtime;
I am not sure where I'm going wrong here. Assistance will be much appreciated. Thanks in advance.
Issues with your code
You're creating a double array when you push elements into 'adsArr':
adsArr.push(elements);
You're throwing a return statement in the for loop:
for (x; x < 1; x++ ){
return x;
// ...
You have a double loop for no reason while inside of the for.
Solution
I was going to explain the solution to this verbally, but after coding an example I realized that there is too much to explain this is another solution similar to yours:
var miniAds = $('#mini_ads'),
elements = miniAds.children(),
i = 2,
x = 0;
elements.hide();
var changeWtime = setInterval(function () {
if ( x < i ) {
$(elements[x]).show();
}
x++;
}, 5000);
Link to example on jsbin.
Hi u should push child divs as below function does and after that i believe u can perform ur task...
var adsArr= [];
$('#mini_ads').children().each(
function(i){
adsArr.push(this);
});
In plain Javascript use .styles()
.css() which is a JQuery method but not Javascript
ref http://www.w3schools.com/js/js_htmldom_css.asp