Loop through javascript improvement - javascript

The below code works properly, but it is hard coded. I would like to be able to create an array of field sets, hide those fields, then each time I click on the "#createEventForm-eventInformation-addElement" button it displays the next one. The problem with the below code is that it is hard coded and thus would break easily and be much larger than using loops. Can someone help me make this better.
$("#fieldset-group1").hide();
$("#fieldset-group2").hide();
$("#fieldset-group3").hide();
$("#fieldset-group4").hide();
$("#fieldset-group5").hide();
$("#fieldset-group6").hide();
$("#fieldset-group7").hide();
$("#fieldset-group8").hide();
$("#fieldset-group9").hide();
$("#createEventForm-eventInformation-addElement").click(
function() {
ajaxAddEventInformation();
if($("#fieldset-group1").is(":hidden"))
{
$("#fieldset-group1").show();
}
else
{
$("#fieldset-group2").show();
}
}
);

You should use the ^= notation of the jquery selectors which means starting with ..
// this will hide all of your fieldset groups
$('[id^="fieldset-group"]').hide();
Then
$("#createEventForm-eventInformation-addElement").click(
function() {
ajaxAddEventInformation();
// find the visible one (current)
var current = $('[id^="fieldset-group"]:visible');
// find its index
var index = $('[id^="fieldset-group"]').index( current );
// hide the current one
current.hide();
// show the next one
$('[id^="fieldset-group"]').eq(index+1).show();
}
);

A quick idea.
Add a class to each fieldset lets say "hiddenfields". Declare a global variable to keep track of which field is shown.
$(".hiddenfields").hide();//hide all
var num = 0;//none shown
$("#createEventForm-eventInformation-addElement").click(
function() {
ajaxAddEventInformation();
num++;
$("#fieldset-group" + num).show();
}
);

Here is one simple solution.
var index = 0;
var fieldsets = [
$("#fieldset-group1").show(),
$("#fieldset-group2"),
$("#fieldset-group3"),
$("#fieldset-group4"),
$("#fieldset-group5"),
$("#fieldset-group6"),
$("#fieldset-group7"),
$("#fieldset-group8"),
$("#fieldset-group9")
];
$("#createEventForm-eventInformation-addElement").click(function() {
ajaxAddEventInformation();
fieldsets[index++].hide();
if (index < fieldsets.length) {
fieldsets[index].show();
}
else {
index = 0;
fieldsets[index].show();
}
});

Add a class 'fieldset' to all fieldsets, then:
$("#createEventForm-eventInformation-addElement").click(
function() {
ajaxAddEventInformation();
$('.fieldset').is(':visible')
.next().show().end()
.hide();
}
);

This will show the first hidden fieldset element whose ID attribute starts with "fieldset-group"...
$("fieldset[id^='fieldset-group']:hidden:first").show();

How about to add (or only use) a class for that fields?
$(".fieldset").hide(); // hides every element with class fieldset
$("#createEventForm-eventInformation-addElement").click( function() {
ajaxAddEventInformation();
// I assume that all fieldset elements are in one container #parentdiv
// gets the first of all remaining hidden fieldsets and shows it
$('#parentdiv').find('.fieldsset:hidden:first').show();
});

Related

Taming the Select - Don't replace disabled OPTION

I have found a great piece of Javascript called Taming Select. I know it's quite old but has worked absolute wonders with rendering the < select > input into a UL dropdown list.
My problem is that my < select > DOM element has dynamically disabled < option > children, yet this piece of code does not discern whether it is disabled or not.
I was wondering a couple of things:
How do I get Javascript to identify disabled < options >?
Should I delete the DOM element completely or inject CSS classes into the newly made list items to disable them with user-select and pointer-events?
I have been on the search for nearly 8 hours and I can't seem to figure out how to do number 1 on my list.
I have tried getElementsByTagName('option').disabled and other variations of getElementsByTagName and nothing happens; even when I modify some examples in W3Schools.
Below is the code for TamingSelect:
function tamingselect()
{
if(!document.getElementById && !document.createTextNode){return;}
// Classes for the link and the visible dropdown
var ts_selectclass='turnintodropdown'; // class to identify selects
var ts_listclass='turnintoselect'; // class to identify ULs
var ts_boxclass='dropcontainer'; // parent element
var ts_triggeron='activetrigger'; // class for the active trigger link
var ts_triggeroff='trigger'; // class for the inactive trigger link
var ts_dropdownclosed='dropdownhidden'; // closed dropdown
var ts_dropdownopen='dropdownvisible'; // open dropdown
/*
Turn all selects into DOM dropdowns
*/
var count=0;
var toreplace=new Array();
var sels=document.getElementsByTagName('select');
for(var i=0;i<sels.length;i++){
if (ts_check(sels[i],ts_selectclass))
{
var hiddenfield=document.createElement('input');
hiddenfield.name=sels[i].name;
hiddenfield.type='hidden';
hiddenfield.id=sels[i].id;
hiddenfield.value=sels[i].options[0].value;
sels[i].parentNode.insertBefore(hiddenfield,sels[i])
var trigger=document.createElement('a');
ts_addclass(trigger,ts_triggeroff);
trigger.href='#';
trigger.onclick=function(){
ts_swapclass(this,ts_triggeroff,ts_triggeron)
ts_swapclass(this.parentNode.getElementsByTagName('ul')[0],ts_dropdownclosed,ts_dropdownopen);
return false;
}
trigger.appendChild(document.createTextNode(sels[i].options[0].text));
sels[i].parentNode.insertBefore(trigger,sels[i]);
var replaceUL=document.createElement('ul');
for(var j=0;j<sels[i].getElementsByTagName('option').length;j++)
{
var newli=document.createElement('li');
var newa=document.createElement('a');
newli.v=sels[i].getElementsByTagName('option')[j].value;
newli.elm=hiddenfield;
newli.istrigger=trigger;
newa.href='#';
newa.appendChild(document.createTextNode(
sels[i].getElementsByTagName('option')[j].text));
newli.onclick=function(){
this.elm.value=this.v;
ts_swapclass(this.istrigger,ts_triggeron,ts_triggeroff);
ts_swapclass(this.parentNode,ts_dropdownopen,ts_dropdownclosed)
this.istrigger.firstChild.nodeValue=this.firstChild.firstChild.nodeValue;
return false;
}
newli.appendChild(newa);
replaceUL.appendChild(newli);
}
ts_addclass(replaceUL,ts_dropdownclosed);
var div=document.createElement('div');
div.appendChild(replaceUL);
ts_addclass(div,ts_boxclass);
sels[i].parentNode.insertBefore(div,sels[i])
toreplace[count]=sels[i];
count++;
}
}
/*
Turn all ULs with the class defined above into dropdown navigations
*/
var uls=document.getElementsByTagName('ul');
for(var i=0;i<uls.length;i++)
{
if(ts_check(uls[i],ts_listclass))
{
var newform=document.createElement('form');
var newselect=document.createElement('select');
for(j=0;j<uls[i].getElementsByTagName('a').length;j++)
{
var newopt=document.createElement('option');
newopt.value=uls[i].getElementsByTagName('a')[j].href;
newopt.appendChild(document.createTextNode(uls[i].getElementsByTagName('a')[j].innerHTML));
newselect.appendChild(newopt);
}
newselect.onchange=function()
{
window.location=this.options[this.selectedIndex].value;
}
newform.appendChild(newselect);
uls[i].parentNode.insertBefore(newform,uls[i]);
toreplace[count]=uls[i];
count++;
}
}
for(i=0;i<count;i++){
toreplace[i].parentNode.removeChild(toreplace[i]);
}
function ts_check(o,c)
{
return new RegExp('\\b'+c+'\\b').test(o.className);
}
function ts_swapclass(o,c1,c2)
{
var cn=o.className
o.className=!ts_check(o,c1)?cn.replace(c2,c1):cn.replace(c1,c2);
}
function ts_addclass(o,c)
{
if(!ts_check(o,c)){o.className+=o.className==''?c:' '+c;}
}
}
window.onload=function()
{
tamingselect();
// add more functions if necessary
}
Any help would be greatly appreciated. I have very very little Javascript experience (to be honest, the closest I have come to studying it was ActionScript 10 years ago).
Thanks in advance.
Michael
You're looking for the .hasAttribute() function which returns a bool.
ie. el.hasAttribute('disabled').
https://developer.mozilla.org/en-US/docs/Web/API/Element/hasAttribute
As mentioned in the comment, to actually iterate through the options to check for the attribute you should do the following:
// returns an array like HTMLCollection of the elements that match the
// tag
let options = document.getElementsByTagName('option')
// uses the array method forEach on the HTMLCollection.
// note that HTMLCollections do not natively have the forEach method
// but still act behave normal arrays when using iteration.
Array.prototype.forEach.call( options, el => {
el.hasAttribute('disabled')
})

JavaScript Hiding li tag by selecting an html attribute not working

I have 3 columns which include dynamically generated list elements (li tags)
these have an attribute that I try to use to hide a row / li when an amount of character is not reached in this element.(by using opacity property)
I have it working...sometimes and sometimes it only works for one column out of the 3...
So I'd appreciate some insight on what's wrong here.
(function() {
// selecting all elements with class
// class="checkout-tariff-meta-maybe-hidden"
var elems = $(".checkout-tariff-meta-maybe-hidden");
// interact between founded elements
for (var k = 0; k < elems.length; k++) {
// getting text content size
var textSize = elems[k].textContent.length;
// if text size is one we will hide element
if (textSize <= 1) {
// hiding
elems[k].style.opacity = "0";
}
}
}());
You can just go straight to the point and do something like:
// Adjust as needed
$(document ).ready(function() {
$('.checkout-tariff-meta-maybe-hidden').filter( function() {
return $(this).text().length<3; } ).hide();
});
Since you're using jQuery, to hide an element you can just do:
$(elems[k]).hide();
Alternatively, if you're looking to hide it without collapsing (since you're changing opacity, I assume this is the case), look into .fadeTo():
$(elems[k]).fadeTo(1, 0);
You might look at ...
if (textSize <= 1) {
elems[k].style.opacity = "0";
} else {
elems[k].style.opacity = "1";
}
... to ensure they get turned back on when longer.

jQuery iteration though all items of a class regardless of their position in the DOM

i'm building a webpage where many span­ needs to be transitioned from one class to another to create a bg-color fadein effect. Distribution of elements of same classes is mixed through the page, but they are all grouped under common classes.
I want to create a behavior that does the following: when you click any elements of class-n, the other elements of that class transitions, with the clicked element acting as the starting point.
This is mostly figured out, thanks to some help on SO; see the jsfiddle.
$(".div").click(function () {
var itemClasses = this.classList;
var itemThread = itemClasses[1];
colorThread($(this), itemThread);
console.log(itemThread);
});
function colorThread($div, tId) {
tId = '.'+tId;
$div.toggleClass('div-clicked');
setTimeout(function () {
(function togglePrev($div) {
$div.toggleClass('div-clicked');
setTimeout(function () {
togglePrev($div.prev(tId));
}, 100);
})($div.prev(tId));
(function toggleNext($div) {
$div.toggleClass('div-clicked');
setTimeout(function () {
toggleNext($div.next(tId));
}, 100);
})($div.next(tId));
}, 100);
}
However, I am still struggling around a particular issue: I don't want the transition to stop if if encounter different class, I just want it not to toggle and keep iterating. If the jsfiddle, that would translate in all of the same color div to transition, regardless of their placement in the DOM tree.
In my togglePrev/toggleNext function, I have tried something along
if($div.hasClass(".classToTransition"))
{
$div.toggleClass(".div-clicked");
}
but couldn't get it to work properly (it doesn't ieterate to the next elements). There is something that I can't seem to grasp in the structure of that conditional. Anyone has a lead?
You really did manage to complicate something that should be pretty simple ?
$(".div").click(function () {
var coll = $('.'+this.className.replace(/(div-clicked|div)/g, '').trim()),
idx = coll.index($(this).toggleClass('div-clicked'));
$.each(coll, function(i) {
setTimeout(function() {
if (idx + i <= coll.length) coll.eq(idx + i).toggleClass('div-clicked');
if (idx - i >= 0) coll.eq(idx - i).toggleClass('div-clicked');
},i*200);
});
});
FIDDLE
It gets all the elements with the same class as the one currently clicked, and the index of the currently clicked, and then just adds and subtract 1 to the current index to get the next and previous elements. The checks are to make sure it stops when it reaches the end.
I don't want the transition to stop if if encounter different class, I just want it not to toggle and keep iterating
You might want to use nextAll(tId).first()/prevAll(tId).first() to select the next to-be-toggled element: http://jsfiddle.net/35uNW/4/. .next() does only look at the next sibling, and if that doesn't match your tId selector, no element will be selected.
If you want to iterate the different-classed elements so that you wait for each one, but don't want to toggle it, you can use your if-condition but you must remove the tId selector from the next()/prev() calls: http://jsfiddle.net/35uNW/3/.
This was a fun one. I did it a slightly different way, getting all of the matched elements and splitting them into before and after arrays.
var $allItems = $(".div");
$(".div").click(function () {
var itemClasses = this.classList;
var itemThread = itemClasses[1];
colorThread($(this), itemThread);
});
function colorThread($div, classname) {
var tId = '.'+classname,
$divs = $allItems.filter(tId),
index = $divs.index($div),
$before = $divs.slice(0, index),
before = $before.get().reverse(),
$after = $divs.slice(index+1);
$div.toggleClass('div-clicked');
$(before).each(function(i, item){
setTimeout(function () {
$(item).toggleClass('div-clicked');
}, i*100);
});
$($after).each(function(i, item){
setTimeout(function () {
$(item).toggleClass('div-clicked');
}, i*100);
});
}
Here's a working fiddle: http://jsfiddle.net/5sUr4/

Recursive IDs and duplicating form elements

I have the following fiddle:
http://jsfiddle.net/XpAk5/63/
The IDs increment appropriately. For the first instance. The issue is when I try to add a sport, while it duplicates, it doesn't duplicate correctly. The buttons to add are not creating themselves correctly. For instance, if I choose a sport, then fill in a position, and add another position, that's all fine (for the first instance). But when I click to add another sport, it shows 2 positions right away, and the buttons aren't duplicating correctly. I think the error is in my HTML, but not sure. Here is the JS I am using to duplicate the sport:
$('#addSport').click(function(){
//increment the value of our counter
$('#kpSport').val(Number($('#kpSport').val()) + 1);
//clone the first .item element
var newItem = $('div.kpSports').first().clone();
//recursively set our id, name, and for attributes properly
childRecursive(newItem,
// Remember, the recursive function expects to be able to pass in
// one parameter, the element.
function(e){
setCloneAttr(e, $('#kpSport').val());
});
// Clear the values recursively
childRecursive(newItem,
function(e){
clearCloneValues(e);
});
Hoping someone has an idea, perhaps I've just got my HTML elements in the wrong order? Thank you for your help! I'm hoping the fiddle is more helpful than just pasting a bunch of code here in the message.
The problem is in your clearCloneValues function. It doesn't differentiate between buttons and other for elements that you do want to clear.
Change it to:
// Sets an element's value to ''
function clearCloneValues(element){
if (element.attr('value') !== undefined && element.attr('type') !== 'button'){
element.val('');
}
}
As #PHPglue pointed out in the comments above, when new positions are added, they are incorrectly replicated (I'm assuming here) to the newly cloned for
There is a similar problem with the add years functionality.
A quick fix would be to initialize a variable with a clone of the original form fields:
var $template = $('div.kpSports').first().clone();
Then change your addSport handler to:
$('#addSport').click(function () {
//increment the value of our counter
$('#kpSport').val(Number($('#kpSport').val()) + 1);
//clone the first .item element
var newItem = $template.clone();
…
});
However, there are no event bindings for the new buttons, so that functionality is still missing for any new set of form elements.
Demo fiddle
Using even a simple, naive string based templates the code can be simplified greatly. Linked is an untested fiddle that shows how it might be done using this approach.
Demo fiddle
The code was simplified to the following:
function getClone(idx) {
var $retVal = $(templates.sport.replace(/\{\{1\}\}/g, idx));
$retVal.find('.jsPositions').append(getItemClone(idx, 0));
$retVal.find('.advtrain').append(getTrainingClone(idx, 0));
return $retVal;
}
function getItemClone(setIdx, itemIdx) {
var retVal = itemTemplate.replace(/\{\{1\}\}/g, setIdx).replace(/\{\{2\}\}/g, itemIdx);
return $(retVal);
}
function getTrainingClone(setIdx, trainingIdx) {
var retVal = trainingTemplate.replace(/\{\{1\}\}/g, setIdx).replace(/\{\{2\}\}/g, trainingIdx);
return $(retVal);
}
$('#kpSportPlayed').on('click', '.jsAddPosition', function() {
var $container = $(this).closest('.kpSports');
var containerIdx = $container.attr('data_idx');
var itemIdx = $container.find('.item').length;
$container.find('.jsPositions').append(getItemClone(containerIdx, itemIdx));
});
$('#kpSportPlayed').on('click', '.jsAddTraining', function() {
var $container = $(this).closest('.kpSports');
var containerIdx = $container.attr('data_idx');
var trainIdx = $container.find('.advtrain > div').length;
$container.find('.advtrain').append(getTrainingClone(containerIdx, trainIdx));
});
$('#addSport').click(function () {
var idx = $('.kpSports').length;
var newItem = getClone(idx);
newItem.appendTo($('#kpSportPlayed'));
});

.first('li) select first item in the list not working

Hope you can help, this code is not working. It is selecting the next image ok, but if it is on the last image and there is no 'next image' then I want to select the first image in the 'li'. I've added an if condition: if (nextLi.value !=='')... but this doesn't seem to be working.
function nextImg(){
$('#background').find('img').animate({'opacity': 0}, 500, function (){
var nextLi = $('.sel').parent().parent().next('li').find('a');
if (nextLi.value !=='') {
var nextLiA = nextLi.attr('rel');
$('#background').find('img').attr({'src':nextLiA});
$('.sel').appendTo(nextLi);
} else {
var nextLi = $('.sel').parent().parent().first('li').find('a');
var nextLiA = nextLi.attr('rel');
$('#background').find('img').attr({'src':nextLiA});
$('.sel').appendTo(nextLi);
}
});
}
You should use length instead, do:
if (!nextLi.length){
//do stuff
}
If the selection is empty nextLi.length will return 0 (i.e. false), so you add an !
nextLi is a jQuery object. If you want to see if it contains any elements, i.e., if anything matched the selector(s) you used then just check its .length property:
if (nextLi.length > 0 ) {
Note also that the three lines inside the if block are the same as the last three lines of the else, so you can simplify your code as follows:
if (nextLi.length === 0) {
nextLi = $('.sel').parent().parent().first('li').find('a');
}
$('#background').find('img').attr({'src': nextLi.attr('rel') });
$('.sel').appendTo(nextLi);
(I've removed the nextLia variable entirely since it was used in only one place - you can just use nextLi.attr('rel') directly when setting the src attribute.)
Try this. It checks for the size of the li and if not found anything, selects the first.
function nextImg(){
$('#background').find('img').animate({'opacity': 0}, 500, function (){
var li = $('.sel').parent().parent().next('li').find('a');
if(li.length == 0) {
//if there is not last item
li = $('.sel').parent().parent().first('li').find('a'); //get the first item
}
var liA = li.attr('rel');
$('#background').find('img').attr({'src':liA});
$('.sel').appendTo(li);
});
}

Categories

Resources