Looping through function causes error and fails on first change - javascript

I have here the following script which is causing me some errors:
var sections = ["#general_info", "#address_records", "#employment_history", "#driver_experience", "#military_experience", "#eeo_survey", "#psp_notice", "#eva"];
for(var i = 0; i < sections.length; i++){
$(sections[i]).find('input, select').each(function(){
$(this).change(function(){
validate();
$(this).closest('.placement').find('.module-heading').removeClass('module-heading-bad');
$(this).closest('.placement').find('.glyphicon').addClass('glyphicon-ok text-success');
$(this).closest('.placement').find('.glyphicon').removeClass('glyphicon-warning-sign text-danger');
$(sections[i]).find('input, select').each(function(){
if($(this).closest('div').hasClass('has-error')){
$(this).closest('.placement').find('.module-heading').addClass('module-heading-bad');
$(this).closest('.placement').find('.glyphicon').addClass('glyphicon-warning-sign text-danger');
$(this).closest('.placement').find('.glyphicon').removeClass('glyphicon-ok text-success');
return false;
}
});
});
});
}
function validate(){
var driving_qs = ['driving_exp_qa', 'driving_exp_qb', 'driving_exp_qc', 'driving_exp_qd'];
for( var i = 0; i < driving_qs.length; i++){
if($('input[name='+driving_qs[i]+']:checked').val()){
$('input[name='+driving_qs[i]+']').closest('.form-group').removeClass('has-error');
$('input[name='+driving_qs[i]+']').closest('.form-group').addClass('has-success');
}else{
$('input[name='+driving_qs[i]+']').closest('.form-group').addClass('has-error');
$('input[name='+driving_qs[i]+']').closest('.form-group').removeClass('has-success');
}
}
var fields = [{
selector: $('.not-empty'),
validations: [ isNotEmpty]
},{
selector: $('.email'),
validations: [ isNotEmpty, isEmail]
},{
selector: $('.number'),
validations: [ isNotEmpty, isNumber]
},{
selector: $('.number-noreq'),
validations: [isNumberNotRequired]
}];
$('.form-control').closest('div').removeClass('has-error');
var i = 0, k = 0, z = 0, j = fields.length, item, selector, fn, info;
for(; i < j; i++){
item = fields[i];
for(k = 0; k < item.validations.length; k++){
fn = item.validations[k];
for( z = 0; z < item.selector.length; z++){
selector = $(item.selector[z]);
info = selector.closest('div');
if(info)
var result = fn(selector.val());
if(result){
info.removeClass("has-error");
info.addClass('has-success');
}else{
info.removeClass('has-success');
info.addClass("has-error")
}
}
}
}
}
The script works perfectly fine if I am running it without the for loop in front of it. Here is a quick step by step of what my code does (note: this is without the for loop):
Locate the section in code and find each input an select field
Assign the change event to each target input and select field
On change find closest span of class placement, and fine the first module heading and perform all the adding and removing of classes, just to refresh the heading to a success heading if no errors exist below.
Find all the inputs and selects and check for errors, if they exists return false, and add the error classes back on everything
This script will work all the way to the end of each section like it is supposed to do. However after I tried to do this with the for loop, it created a success scenario after only one input. Why is this happening, and is it even possible to have this function inside a loop like I am currently doing?
Also below I have included samples of the html mark-up
<!-- this tag serves no purpose other than being a bookmark for scripting -->
<span class='placement'>
<!-- Section 1: General Information -->
<div id='general-heading' class='row module-heading module-heading-bad general' data-toggle='#general_info'>
<div class='form-group'>
<div class='col-md-12'>
<h4 class='text-info '>General Information<div id='general-confirmation' class='glyphicon glyphicon-warning-sign pull-right text-danger'></div></h4>
</div>
</div>
</div>
<div id='general_info' class='app-section'>
<div class='form-group'>
<div class='col-xs-12'>
<div class='form-group row'>
<div class='col-sm-6 col-xs-12'>
<label class='control-label'>First Name<span class='req'> *</span></label><br />
<input type='text' class='form-control not-empty' id='first_name' value="<?=$first_name?>"/>
</div>
<div class='col-sm-6 col-xs-12'>
<label class='control-label'>Middle Name</label><br />
<input type='text' class='form-control' id='middle_name' value="<?=$middle_name?>"/>
</div>
</div>
</div>
</div>
</span>

The problem in this block of code:
for(var i = 0; i < sections.length; i++){
$(sections[i]).find('input, select').each(function(){
$(this).change(function(){
...
$(sections[i]).find('input, select').each(function(){
...
}
});
});
});
}
Is that it uses the variable i, which will have changed when the function() inside change is run.
In your case, the simplest way to fix it would be by using the forEach function instead of a for loop, and not using the index at all:
sections.forEach(function(section){
$(section).find('input, select').each(function(){
$(this).change(function(){
...
$(section).find('input, select').each(function(){
...
}
});
});
});
})
This will ensure that the i you mean is different each time.

Related

add div id and for attribute sequentially using js

I want to dynamically add the id and for attribute for each input and label element.
<div id="splash">
<div class="tab">
<input id="tab-1">
<label for="tab-1"><label>
</div>
<div class="tab">
<input id="tab-2">
<label for="tab-2"><label>
</div>
<div class="tab">
<input id="tab-3">
<label for="tab-3"><label>
</div>
</div>
So basically I would want the id for the input to be tab-# with the # increasing by 1 for each input field and the same for the "for=" attribute for the label.
It's super easy. Just iterate through each .tab, using each's index argument, and modify the attributes of the elements.
$('.tab').each(function (index) {
var tabName = 'tab-' + (index + 1);
$('input', this).attr('id', tabName);
$('label', this).attr('for', tabName);
});
Jsbin: http://jsbin.com/rawatag/4/edit?html,js,output
Ok.
I won't give you a straight answer but this should be more useful in future.
Basically make the container <div id=splash>
Then run this command document.getElementById("parentID").innerHTML += "Something here"
This will add the content (pay attention to. The += sign) to the div (splash)
Then, just wrap this in a loop using a counter to get the desired result
Eg: ...innerHTML += "<div id=tab-" + counter + "></div>"
Note that this can be done in raw JS. No JQuery required.
No need for jQuery here:
es5 (jsfiddle)
function assignInputsAndLabels(root) {
var children = root.children;
var tabNumber = 1;
for (var i = 0; i < children.length; i++) {
if (children[i].classList.contains('tab')) {
children[i].getElementsByTagName('input')[0].setAttribute('id', 'tab-' + tabNumber);
children[i].getElementsByTagName('label')[0].setAttribute('for', 'tab-' + tabNumber);
tabNumber++;
}
}
}
assignInputsAndLabels(document.getElementById('splash'));
es6
function assignInputsAndLabels(root) {
const children = root.children;
let tabNumber = 1;
for (let i = 0; i < children.length; i++) {
if (children[i].classList.contains('tab')) {
children[i].getElementsByTagName('input')[0].setAttribute('id', `tab-${tabNumber}`);
children[i].getElementsByTagName('label')[0].setAttribute('for', `tab-${tabNumber}`);
tabNumber++;
}
}
}
assignInputsAndLabels(document.getElementById('splash'));
The parameter to the function is the wrapper of the elements that have the class of tab. In your case, you'd pass in the DOM node of the element with id of splash. So you'd call the function like this:
assignInputsAndLabels(document.getElementById('splash'));
I have done it using javascript.Check it below
function init(){
var sel = document.getElementsByClassName("tab");
var i=1;
for(let obj of sel){
var attr = "tab-"+i;
obj.getElementsByTagName('input')[0].setAttribute("id",attr);
obj.getElementsByTagName('label')[0].setAttribute("for",attr);
i++;
}
}
addEventListener("load",init);
<div class="tab">
<input type="text">
<label></label>
</div>
<div class="tab">
<input type="text">
<label></label>
</div>

Get ID From Selected Checkbox on ng-change

I'm attempting to extract the ID of checkbox when it is selected, but I can't seem to find a way that fits what I'm trying to do.
First I have the HTML / Angular for the check boxes. The check boxes are generated by three tiers. First there's a service level, then the day of the week and then the service itself (which are what the check boxes are). The service level makes an accordion, the days of the week are loaded into tabs and the check boxes themselves come in as normal.
<div class="delivery-rules">
<div class="panel-group" id="accordion">
<div class="panel panel-default" ng-repeat="level in settings.serviceLevels">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#{{level.LevelTmsCode}}">{{level.LevelName}}</a>
</h4>
</div>
<div id="{{level.LevelTmsCode}}" class="panel-collapse collapse in">
<div class="panel-body">
<ul class="nav nav-tabs">
<li id="{{day.Day}}-{{level.LevelTmsCode}}-tab" ng-repeat="day in settings.serviceDays">
<a id="{{day.Day}}-{{level.LevelTmsCode}}" href="#tabContent-{{day.Day}}-{{level.LevelTmsCode}}" ng-click="settings.changeTab(day, level, $event)">{{day.Day}}</a>
</li>
</ul>
<div class="tabContent" id="tabContent-{{day.Day}}-{{level.LevelTmsCode}}" ng-repeat="day in settings.serviceDays">
<h4>{{day.Day}}</h4>
<div class="time-check" ng-repeat="service in settings.services">
<input type="checkbox" value="None" ng-change="settings.showChecked(settings.rules, $event)" ng-model="settings.selected[$index]" class="time-check-input" id="{{level.LevelTmsCode}}-{{day.Day}}-{{service.TimeValidation}}" name="check"/>
<label for="{{level.LevelTmsCode}}-{{day.Day}}-{{service.TimeValidation}}" class="time-check-input"></label> <span>{{service.TimeValidation}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
The arrays that build those check boxes, tabs and accordion are loaded with data from a standard http post request. Then once that is complete I place all the possible combinations of all three arrays into one big array and set their checked attribute to false.
// Get Service Levels to Build Delivery Rules Accordion
settings.getDeliveryServices = function() {
$http.get(resourceBase + "api/service/levels").success(function(data) {
settings.serviceLevels = data;
// Get Service Days
$http.get(resourceBase + "api/service/days").success(function(days) {
settings.serviceDays = days;
// Build the Accordion
setTimeout(() => settings.triggerClick(settings.serviceLevels), 500);
$http.get(resourceBase + "api/service/services").success(function (services) {
settings.services = services;
// Build a collection of all possible rules
for (var a = 0; a < settings.serviceLevels.length; a++) {
settings.rulesTmsCode.push(settings.serviceLevels[a].LevelTmsCode + "-");
}
for (var b = 0; b < settings.serviceDays.length; b++) {
settings.rulesDay.push(settings.serviceDays[b].Day + "-");
}
for (var c = 0; c < settings.services.length; c++) {
settings.rulesTime.push(settings.services[c].TimeValidation);
}
var allArrays = [settings.rulesTmsCode, settings.rulesDay, settings.rulesTime];
function allPossibleCases(arr) {
if (arr.length === 1) {
return arr[0];
} else {
var result = [];
var allCasesOfRest = allPossibleCases(arr.slice(1));
for (var i = 0; i < allCasesOfRest.length; i++) {
for (var j = 0; j < arr[0].length; j++) {
result.push(arr[0][j] + allCasesOfRest[i]);
}
}
return result;
}
}
var uncheckedRules = allPossibleCases(allArrays);
for (var i = 0; i < uncheckedRules.length; i++) {
settings.rules.push({
id: uncheckedRules[i],
checked: false
});
}
});
});
});
}
When each box is checked I'm trying to manipulate the combination array so that the selected combination is set to true.
// Check and Filter Rules to send
settings.showChecked = function (object, $event) {
for (var i = 0; i < settings.rules.length; i++) {
if (settings.rules.hasOwnProperty(i)) {
if (typeof settings.rules[i].id == settings.selected[i]) {
settings.showChecked(settings.rules[i], settings.selected[i]);
}
if (settings.rules[i].id === settings.selected[i]) {
settings.rules[i].checked = true;
}
}
}
console.clear();
console.log(settings.rules);
}
Currently, nothing is set to true as I can't seem to be able to get the ID from the checkbox to compare it with the string stored in the ID value of the combination array. So basically I need the ID of the checkbox that was selected and I need to pass that through to the ng-change event.
Try Some thing like this..
<input id={{emp.name}} type=checkbox value="{{emp.name}}" ng-change="settings.showChecked(settings.rules, $event)>
settings.showChecked=function(object,$event)
{
var el = event.target.id
}
the other way is you can pass id value in place event like below
<select id="hairColorComponent" ng-model="hairColor"
ng-options="option.name for option in hairColorData"
ng-change="updateUserData('hairColorComponent')">
$scope.updateUserData = function (id) {
var element = jQuery('#'+id);
};

for loop is iterating over the same value

So basically I have a set of questions(<div>s), all with the same class name and I want to simply loop through all of those classes and append the questions(<div>s) inside a container but at the minute it seems to just loop through it but only display the 1st question over and over or however many questions I put in.
if($('.option').hasClass('c_questions')){
var y = document.getElementsByClassName('c_questions');
for(var i = 0; i < y.length; i++){
$('.main-body').append($('.c_questions').html());
}
}
$('.c_questions').each(function(a){
$('.main-body').append($(this).html()+" ");
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="c_questions">hi</div>
<div class="c_questions">hey</div>
<div class="c_questions">test</div>
<p class="main-body"></p>
by edit into code. (squint suggested.)
if($('.option').hasClass('c_questions')){
var y = document.getElementsByClassName('c_questions');
for(var i = 0; i < y.length; i++){
$('.main-body').append(y[i].innerHTML);
} ^^^^^change
}
Don't use a for loop, look at jQuery's each.
So this would loop through each element with the class example below:
$( ".test" ).each(function( index ) {
console.log($( this ).text() );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test">hi</div>
<div class="test">hey</div>
<div class="test">test</div>

How can I check all boxes in a nested ng-repeat by clicking a button?

So what I need to do is get only all of the shown (using ng-show) students checkboxes checked by clicking a toggleAll() button at the top of the page.
Here is my code:
<div ng-repeat="course in content" ng-show="course.show" >
<div ng-repeat="student in course.students" ng-show="student.show" ng-click="toggleStudent(student)">
<input type="checkbox">
........
I tried using:
$scope.toggleAll = function () {
for (var i = 0; i < $scope.course.students.length; i++) {
...
}
};
but length is undefined.
Any help would be greatly appreciated!
course is a local variable so it is not accessible on your main scope.
Assuming you want to check all for one course only, you should pass that course into the function.
<div ng-repeat="course in content" ng-show="course.show" >
<input type='checkbox' ng-click='toggleAll(course)'>
<div ng-repeat="student in course.students" ng-show="student.show" ng-click="toggleStudent(student)">
...
$scope.toggleAll = function (course) {
for (var i = 0; i < course.students.length; i++) {
...
}
};

clone in for loop

I have a row of text boxes , I have a function to clone them based on what number comes into the function. So if there are going to be 4 users then I want the row to clone 4 times to enter the information of the 4 users. But I also want some way to be able to submit this form , I am having trouble figuring out how to give each row a unique class or id for each text box so I can read through them when submitting.
I was thinking adding "1" to each class (or id) to everything in the first row, then "2" to everything in the second. But I am not too sure as to how to do this. I have an example Here In jsFiddle , Since I have tried to add the for loop and clone a certain amount of times , now the clone isn't even working at all- If anyone has any suggestions , it would really help me out.
<div class="RegisterContainer">
<div class="RegisterHead"><a>Register Attendees</a></div>
<div class="placenewrows"></div>
</div>
<br />
<input type="button" onclick="fnCloneTemplate({'number' : '3'});" value="make 3 rows">
<div class="_template">
<a class="left1">First Name:</a>
<a class="left2"></a><a class="left2">Last Name:</a>
<a class="left3">Phone #</a><a class="left4">Email:</a>
<a class="left5">Optional Comment</a><br />
<input type="text" class="tFirstName left1"/>
<input type="text" class="tLastName left2"/>
<div class="phonenumberbox left3">
<input type="text" class="first3digits" maxlength="3" />
<a style="position:relative;top:-1px;">-</a>
<input type="text" class="next3digits" maxlength="3" />
<a style="position:relative;top:-1px;">-</a>
<input type="text" class="last4digits" maxlength="4" />
</div> <input type="text" class="tEmail left4"/>
function fnCloneTemplate(x){
var NumofClones = (x.number * 1);
for(i=0; i <= NumofClones; i++)
{
var newrow = $('._template').clone().removeclass('_template');
$('.placenewrows').append(newrow);
}
}
​
There is a typo in your code:
var newrow = $('._template').clone().removeclass('_template');
//----^
removeclass should be removeClass.
http://jsfiddle.net/y543n/
Also you haven't loaded jQuery in your fiddle and there is a scoping issue there, you are using HTML onclick attribute and your function in that context is not defined. You can use jQuery click method instead:
$('input[type=button]').click(function(e){
e.preventDefault();
// ....
})
$('input[type=button]').click(function(e) {
var numofClones = 3;
e.preventDefault();
var b = $('.placenewrows input[type=text]').length;
var newrow = $('._template').clone().removeClass('_template').find('input[type=text]').addClass(function(i, cur) {
return 'something' + ++b
}).end()
for (i = 0; i < numofClones; i++) {
$('.placenewrows').append(newrow);
}
})​
http://jsfiddle.net/bgCXX/
You can change your function like below, to avoid multiple time cloning.
function fnCloneTemplate(e){
var NumofClones = (e.data.number * 1),
newrow= $('._template').clone().removeClass('_template'); // in your code
// removeClass spelling
// mistaken
for (i=0; i<NumofClones; i++)
{
$('.placenewrows').append(newrow);
}
}
Using on():
HTML
<input type="button"value="make 3 rows" id="make_clone">
jQuery
function fnCloneTemplate(e){
var NumofClones = (e.data.number * 1),
newrow= $('._template').clone().removeClass('_template');
for (i=0; i<NumofClones; i++)
{
$('.placenewrows').append(newrow);
}
}
$('#make_clone').on('click',{'number' : '3'}, fnCloneTemplate);
THE DEMO
Full Code for clone and unique class
function fnCloneTemplate(x) {
var NumofClones = (x.data.number * 1),
clone = $('._template').clone().removeClass('_template');
for (i = 0; i <= NumofClones; i++) {
var newrow = clone
.find('input[type=text]')
.attr('class', function(i, oldClass) {
return oldClass.replace(/\d/, function(char) {
return +char + i ;
});
return newClass
})
.end();
$('.placenewrows').append(newrow);
}
}

Categories

Resources